PK^J ;UUentrypoints.py"""Discover and load entry points from installed packages.""" # Copyright (c) Thomas Kluyver and contributors # Distributed under the terms of the MIT license; see LICENSE file. from contextlib import contextmanager import glob from importlib import import_module import io import itertools import os.path as osp import re import sys import warnings import zipfile if sys.version_info[0] >= 3: import configparser else: from backports import configparser entry_point_pattern = re.compile(r""" (?P\w+(\.\w+)*) (:(?P\w+(\.\w+)*))? \s* (\[(?P.+)\])? $ """, re.VERBOSE) __version__ = '0.2.3' class BadEntryPoint(Exception): """Raised when an entry point can't be parsed. """ def __init__(self, epstr): self.epstr = epstr def __str__(self): return "Couldn't parse entry point spec: %r" % self.epstr @staticmethod @contextmanager def err_to_warnings(): try: yield except BadEntryPoint as e: warnings.warn(str(e)) class NoSuchEntryPoint(Exception): """Raised by :func:`get_single` when no matching entry point is found.""" def __init__(self, group, name): self.group = group self.name = name def __str__(self): return "No {!r} entry point found in group {!r}".format(self.name, self.group) class CaseSensitiveConfigParser(configparser.ConfigParser): optionxform = staticmethod(str) class EntryPoint(object): def __init__(self, name, module_name, object_name, extras=None, distro=None): self.name = name self.module_name = module_name self.object_name = object_name self.extras = extras self.distro = distro def __repr__(self): return "EntryPoint(%r, %r, %r, %r)" % \ (self.name, self.module_name, self.object_name, self.distro) def load(self): """Load the object to which this entry point refers. """ mod = import_module(self.module_name) obj = mod if self.object_name: for attr in self.object_name.split('.'): obj = getattr(obj, attr) return obj @classmethod def from_string(cls, epstr, name, distro=None): """Parse an entry point from the syntax in entry_points.txt :param str epstr: The entry point string (not including 'name =') :param str name: The name of this entry point :param Distribution distro: The distribution in which the entry point was found :rtype: EntryPoint :raises BadEntryPoint: if *epstr* can't be parsed as an entry point. """ m = entry_point_pattern.match(epstr) if m: mod, obj, extras = m.group('modulename', 'objectname', 'extras') if extras is not None: extras = re.split(',\s*', extras) return cls(name, mod, obj, extras, distro) else: raise BadEntryPoint(epstr) class Distribution(object): def __init__(self, name, version): self.name = name self.version = version def __repr__(self): return "Distribution(%r, %r)" % (self.name, self.version) def iter_files_distros(path=None, repeated_distro='first'): if path is None: path = sys.path # Distributions found earlier in path will shadow those with the same name # found later. If these distributions used different module names, it may # actually be possible to import both, but in most cases this shadowing # will be correct. distro_names_seen = set() for folder in path: if folder.rstrip('/\\').endswith('.egg'): # Gah, eggs egg_name = osp.basename(folder) if '-' in egg_name: distro = Distribution(*egg_name.split('-')[:2]) if (repeated_distro == 'first') \ and (distro.name in distro_names_seen): continue distro_names_seen.add(distro.name) else: distro = None if osp.isdir(folder): ep_path = osp.join(folder, 'EGG-INFO', 'entry_points.txt') if osp.isfile(ep_path): cp = CaseSensitiveConfigParser() cp.read(ep_path) yield cp, distro elif zipfile.is_zipfile(folder): z = zipfile.ZipFile(folder) try: info = z.getinfo('EGG-INFO/entry_points.txt') except KeyError: continue cp = CaseSensitiveConfigParser() with z.open(info) as f: fu = io.TextIOWrapper(f) cp.read_file(fu, source=osp.join(folder, 'EGG-INFO', 'entry_points.txt')) yield cp, distro for path in itertools.chain( glob.iglob(osp.join(folder, '*.dist-info', 'entry_points.txt')), glob.iglob(osp.join(folder, '*.egg-info', 'entry_points.txt')) ): distro_name_version = osp.splitext(osp.basename(osp.dirname(path)))[0] if '-' in distro_name_version: distro = Distribution(*distro_name_version.split('-', 1)) if (repeated_distro == 'first') \ and (distro.name in distro_names_seen): continue distro_names_seen.add(distro.name) else: distro = None cp = CaseSensitiveConfigParser() cp.read(path) yield cp, distro def get_single(group, name, path=None): """Find a single entry point. Returns an :class:`EntryPoint` object, or raises :exc:`NoSuchEntryPoint` if no match is found. """ for config, distro in iter_files_distros(path=path): if (group in config) and (name in config[group]): epstr = config[group][name] with BadEntryPoint.err_to_warnings(): return EntryPoint.from_string(epstr, name, distro) raise NoSuchEntryPoint(group, name) def get_group_named(group, path=None): """Find a group of entry points with unique names. Returns a dictionary of names to :class:`EntryPoint` objects. """ result = {} for ep in get_group_all(group, path=path): if ep.name not in result: result[ep.name] = ep return result def get_group_all(group, path=None): """Find all entry points in a group. Returns a list of :class:`EntryPoint` objects. """ result = [] for config, distro in iter_files_distros(path=path): if group in config: for name, epstr in config[group].items(): with BadEntryPoint.err_to_warnings(): result.append(EntryPoint.from_string(epstr, name, distro)) return result if __name__ == '__main__': import pprint pprint.pprint(get_group_all('console_scripts')) PK#OCI)Q4JJ#entrypoints-0.2.3.dist-info/LICENSEThe MIT License (MIT) Copyright (c) 2015 Thomas Kluyver and contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK!H'Ub!entrypoints-0.2.3.dist-info/WHEEL HM K-*ϳR03rOK-J,/RH,Q034 /, (-JLR()*M ILR(4KM̫#DPK!HQ ~$entrypoints-0.2.3.dist-info/METADATAMPN0+V8āT(JSD ђl+۩4q̆V0y%.y T$tp5R~ 9 JK_9@]AoX0ѠvF>`S=E6E62/Jv0F$#X۷-'`tNH#x{бg#<yƯ]ZR=ݭ`iA}J 4Ec'g|ίo]S0v3PK!H9|"entrypoints-0.2.3.dist-info/RECORDvC@}e(&bf`"rB+~:wjAf0'EݼtV_DPh2>tLMrUAŤ *kITD(f0 U_lˠ]nXn*.R{xO.cb:tL1 KnQj/spγȱqhw?KK6Y?Cb, RNK{/֜ |*m^x Cs= R>5>|/PK^J ;UUentrypoints.pyPK#OCI)Q4JJ#entrypoints-0.2.3.dist-info/LICENSEPK!H'Ub! entrypoints-0.2.3.dist-info/WHEELPK!HQ ~$ entrypoints-0.2.3.dist-info/METADATAPK!H9|"!entrypoints-0.2.3.dist-info/RECORDPK~!#