Codebase list entrypoints / 63efcfd
New upstream version 0.3 Julien Puydt 5 years ago
9 changed file(s) with 106 addition(s) and 40 deletion(s). Raw diff Collapse all Expand all
00 language: python
11 python:
2 - "3.6"
3 - "3.5"
24 - "3.4"
3 - "3.3"
45 - "2.7"
56 install:
67 - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install configparser; fi
0 Entry points are a way for Python packages to advertise objects with some
1 common interface. The most common examples are ``console_scripts`` entry points,
2 which define shell commands by identifying a Python function to run.
3
4 *Groups* of entry points, such as ``console_scripts``, point to objects with
5 similar interfaces. An application might use a group to find its plugins, or
6 multiple groups if it has different kinds of plugins.
7
8 The **entrypoints** module contains functions to find and load entry points.
9 You can install it from PyPI with ``pip install entrypoints``.
10
11 To advertise entry points when distributing a package, see
12 `entry_points in the Python Packaging User Guide
13 <https://packaging.python.org/en/latest/distributing.html#entry-points>`_.
3131 .. attribute:: object_name
3232
3333 The dotted object name within the module, or *None* if the entry point
34 refers to a module itselfs.
34 refers to a module itself.
3535
3636 .. attribute:: extras
3737
3838 Extra setuptools features related to this entry point as a list, or *None*
3939
40 .. attribute:: distribution
40 .. attribute:: distro
4141
4242 The distribution which advertised this entry point -
4343 a :class:`Distribution` instance or None
5959 # built documents.
6060 #
6161 # The short X.Y version.
62 version = '0.2'
62 from entrypoints import __version__ as version
6363 # The full version, including alpha/beta/rc tags.
64 release = version + '.3'
64 release = version
6565
6666 # The language for content autogenerated by Sphinx. Refer to documentation
6767 # for a list of supported languages.
00 entrypoints |version|
11 =====================
22
3 Entry points are a way for Python packages to advertise objects with some
4 common interface. The most common examples are ``console_scripts`` entry points,
5 which define shell commands by identifying a Python function to run.
6
7 *Groups* of entry points, such as ``console_scripts``, point to objects with
8 similar interfaces. An application might use a group to find its plugins, or
9 multiple groups if it has different kinds of plugins.
10
11 The **entrypoints** module contains functions to find and load entry points.
12 You can install it from PyPI with ``pip install entrypoints``.
13
14 To advertise entry points when distributing a package, see
15 `entry_points in the Python Packaging User Guide
16 <https://packaging.python.org/en/latest/distributing.html#entry-points>`_.
3 .. include:: ../README.rst
174
185 Contents:
196
2525 $
2626 """, re.VERBOSE)
2727
28 __version__ = '0.2.3'
28 file_in_zip_pattern = re.compile(r"""
29 (?P<dist_version>[^/\\]+)\.(dist|egg)-info
30 [/\\]entry_points.txt$
31 """, re.VERBOSE)
32
33 __version__ = '0.3'
2934
3035 class BadEntryPoint(Exception):
3136 """Raised when an entry point can't be parsed.
7984 for attr in self.object_name.split('.'):
8085 obj = getattr(obj, attr)
8186 return obj
82
87
8388 @classmethod
8489 def from_string(cls, epstr, name, distro=None):
8590 """Parse an entry point from the syntax in entry_points.txt
9499 if m:
95100 mod, obj, extras = m.group('modulename', 'objectname', 'extras')
96101 if extras is not None:
97 extras = re.split(',\s*', extras)
102 extras = re.split(r',\s*', extras)
98103 return cls(name, mod, obj, extras, distro)
99104 else:
100105 raise BadEntryPoint(epstr)
103108 def __init__(self, name, version):
104109 self.name = name
105110 self.version = version
106
111
107112 def __repr__(self):
108113 return "Distribution(%r, %r)" % (self.name, self.version)
109114
131136 distro_names_seen.add(distro.name)
132137 else:
133138 distro = None
134
139
135140 if osp.isdir(folder):
136141 ep_path = osp.join(folder, 'EGG-INFO', 'entry_points.txt')
137142 if osp.isfile(ep_path):
138 cp = CaseSensitiveConfigParser()
139 cp.read(ep_path)
143 cp = CaseSensitiveConfigParser(delimiters=('=',))
144 cp.read([ep_path])
140145 yield cp, distro
141146
142147 elif zipfile.is_zipfile(folder):
145150 info = z.getinfo('EGG-INFO/entry_points.txt')
146151 except KeyError:
147152 continue
148 cp = CaseSensitiveConfigParser()
153 cp = CaseSensitiveConfigParser(delimiters=('=',))
149154 with z.open(info) as f:
150155 fu = io.TextIOWrapper(f)
151156 cp.read_file(fu,
152157 source=osp.join(folder, 'EGG-INFO', 'entry_points.txt'))
153158 yield cp, distro
154
159
160 # zip imports, not egg
161 elif zipfile.is_zipfile(folder):
162 with zipfile.ZipFile(folder) as zf:
163 for info in zf.infolist():
164 m = file_in_zip_pattern.match(info.filename)
165 if not m:
166 continue
167
168 distro_name_version = m.group('dist_version')
169 if '-' in distro_name_version:
170 distro = Distribution(*distro_name_version.split('-', 1))
171
172 if (repeated_distro == 'first') \
173 and (distro.name in distro_names_seen):
174 continue
175 distro_names_seen.add(distro.name)
176 else:
177 distro = None
178
179 cp = CaseSensitiveConfigParser(delimiters=('=',))
180 with zf.open(info) as f:
181 fu = io.TextIOWrapper(f)
182 cp.read_file(fu, source=osp.join(folder, info.filename))
183 yield cp, distro
184
185 # Regular file imports (not egg, not zip file)
155186 for path in itertools.chain(
156187 glob.iglob(osp.join(folder, '*.dist-info', 'entry_points.txt')),
157188 glob.iglob(osp.join(folder, '*.egg-info', 'entry_points.txt'))
166197 distro_names_seen.add(distro.name)
167198 else:
168199 distro = None
169 cp = CaseSensitiveConfigParser()
170 cp.read(path)
200 cp = CaseSensitiveConfigParser(delimiters=('=',))
201 cp.read([path])
171202 yield cp, distro
172203
173204 def get_single(group, name, path=None):
+0
-8
flit.ini less more
0 [metadata]
1 module = entrypoints
2 author = Thomas Kluyver
3 author-email = thomas@kluyver.me.uk
4 home-page = https://github.com/takluyver/entrypoints
5 classifiers = License :: OSI Approved :: MIT License
6 requires-python = >=2.7
7 requires = configparser (>=3.5); python_version == '2.7'
0 [build-system]
1 requires = ["flit"]
2 build-backend = "flit.buildapi"
3
4 [tool.flit.metadata]
5 module = "entrypoints"
6 author = "Thomas Kluyver"
7 author-email = "thomas@kluyver.me.uk"
8 home-page = "https://github.com/takluyver/entrypoints"
9 description-file = "README.rst"
10 classifiers = [
11 "License :: OSI Approved :: MIT License",
12 "Programming Language :: Python :: 2",
13 "Programming Language :: Python :: 3"
14 ]
15 requires-python = ">=2.7"
16 requires = ["configparser (>=3.5); python_version == '2.7'"]
17
18 [tool.flit.metadata.urls]
19 Documentation = "https://entrypoints.readthedocs.io/en/latest/"
33 import os.path as osp
44 import pytest
55 import warnings
6 from zipfile import ZipFile
67
78 import entrypoints
89
3435 assert ep.object_name == 'abc'
3536
3637 ep2 = entrypoints.get_single('entrypoints.test1', 'njn', sample_path)
37 assert ep.module_name == 'foo'
38 assert ep.object_name == 'abc'
38 assert ep2.module_name == 'qux.extn'
39 assert ep2.object_name == 'Njn.load'
3940
4041 def test_dot_prefix():
4142 ep = entrypoints.get_single('blogtool.parsers', '.rst', sample_path)
4849 def test_case_sensitive():
4950 group = entrypoints.get_group_named('test.case_sensitive', sample_path)
5051 assert set(group.keys()) == {'Ptangle', 'ptangle'}
52
53 def test_load_zip(tmpdir):
54 whl_file = str(tmpdir / 'parmesan-1.2.whl')
55 with ZipFile(whl_file, 'w') as whl:
56 whl.writestr('parmesan-1.2.dist-info/entry_points.txt',
57 b'[entrypoints.test.inzip]\na = edam:gouda')
58 whl.writestr('gruyere-2!1b4.dev0.egg-info/entry_points.txt',
59 b'[entrypoints.test.inzip]\nb = wensleydale:gouda')
60
61 ep = entrypoints.get_single('entrypoints.test.inzip', 'a', [str(whl_file)])
62 assert ep.module_name == 'edam'
63 assert ep.object_name == 'gouda'
64 assert ep.distro.name == 'parmesan'
65 assert ep.distro.version == '1.2'
66
67 ep2 = entrypoints.get_single('entrypoints.test.inzip', 'b', [str(whl_file)])
68 assert ep2.module_name == 'wensleydale'
69 assert ep2.object_name == 'gouda'
70 assert ep2.distro.name == 'gruyere'
71 assert ep2.distro.version == '2!1b4.dev0'
5172
5273 def test_load():
5374 ep = entrypoints.EntryPoint('get_ep', 'entrypoints', 'get_single', None)