def test_read_metadata(self): fields = { 'name': 'project', 'version': '1.0', 'description': 'desc', 'summary': 'xxx', 'download_url': 'http://example.com', 'keywords': ['one', 'two'], 'requires_dist': ['foo'] } metadata = Metadata(mapping=fields) PKG_INFO = StringIO() metadata.write_file(PKG_INFO) PKG_INFO.seek(0) metadata = Metadata(fileobj=PKG_INFO) self.assertEqual(metadata['name'], 'project') self.assertEqual(metadata['version'], '1.0') self.assertEqual(metadata['summary'], 'xxx') self.assertEqual(metadata['download_url'], 'http://example.com') self.assertEqual(metadata['keywords'], ['one', 'two']) self.assertEqual(metadata['platform'], []) self.assertEqual(metadata['obsoletes'], []) self.assertEqual(metadata['requires-dist'], ['foo'])
def test_metadata_read_write(self): PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO') metadata = Metadata(PKG_INFO) out = StringIO() metadata.write_file(out) out.seek(0) res = Metadata() res.read_file(out) self.assertEqual(metadata.values(), res.values())
def __init__(self, path): self.path = path if _cache_enabled and path in _cache_path_egg: self.metadata = _cache_path_egg[path].metadata self.name = self.metadata['Name'] self.version = self.metadata['Version'] return requires = None if path.endswith('.egg'): if os.path.isdir(path): meta_path = os.path.join(path, 'EGG-INFO', 'PKG-INFO') self.metadata = Metadata(path=meta_path) req_path = os.path.join(path, 'EGG-INFO', 'requires.txt') requires = parse_requires(req_path) else: # FIXME handle the case where zipfile is not available zipf = zipimport.zipimporter(path) fileobj = StringIO( zipf.get_data('EGG-INFO/PKG-INFO').decode('utf8')) self.metadata = Metadata(fileobj=fileobj) try: requires = zipf.get_data('EGG-INFO/requires.txt') except IOError: requires = None self.name = self.metadata['Name'] self.version = self.metadata['Version'] elif path.endswith('.egg-info'): if os.path.isdir(path): path = os.path.join(path, 'PKG-INFO') req_path = os.path.join(path, 'requires.txt') requires = parse_requires(req_path) self.metadata = Metadata(path=path) self.name = self.metadata['Name'] self.version = self.metadata['Version'] else: raise ValueError('path must end with .egg-info or .egg, got %r' % path) if requires: if self.metadata['Metadata-Version'] == '1.1': # we can't have 1.1 metadata *and* Setuptools requires for field in ('Obsoletes', 'Requires', 'Provides'): if field in self.metadata: del self.metadata[field] self.metadata['Requires-Dist'] += requires if _cache_enabled: _cache_path_egg[self.path] = self
def test_project_url(self): metadata = Metadata() metadata['Project-URL'] = [('one', 'http://ok')] self.assertEqual(metadata['Project-URL'], [('one', 'http://ok')]) metadata.set_metadata_version() self.assertEqual(metadata['Metadata-Version'], '1.2') # make sure this particular field is handled properly when written fp = StringIO() metadata.write_file(fp) self.assertIn('Project-URL: one,http://ok', fp.getvalue().split('\n')) fp.seek(0) metadata = Metadata() metadata.read_file(fp) self.assertEqual(metadata['Project-Url'], [('one', 'http://ok')])
def test_empty_install(self): pkg_dir, dist = self.create_dist(name='foo', version='1.0') install_dir = self.mkdtemp() install = DummyInstallCmd(dist) dist.command_obj['install_dist'] = install cmd = install_distinfo(dist) dist.command_obj['install_distinfo'] = cmd cmd.install_dir = install_dir cmd.ensure_finalized() cmd.run() self.checkLists(os.listdir(install_dir), ['foo-1.0.dist-info']) dist_info = os.path.join(install_dir, 'foo-1.0.dist-info') self.checkLists(os.listdir(dist_info), ['METADATA', 'RECORD', 'REQUESTED', 'INSTALLER']) with open(os.path.join(dist_info, 'INSTALLER')) as fp: self.assertEqual(fp.read(), 'distutils') with open(os.path.join(dist_info, 'REQUESTED')) as fp: self.assertEqual(fp.read(), '') meta_path = os.path.join(dist_info, 'METADATA') self.assertTrue(Metadata(path=meta_path).check())
def __init__(self, name, version, metadata=None, hidden=False, index=None, **kwargs): """ :param name: the name of the distribution :param version: the version of the distribution :param metadata: the metadata fields of the release. :type metadata: dict :param kwargs: optional arguments for a new distribution. """ self.set_index(index) self.name = name self._version = None self.version = version if metadata: self.metadata = Metadata(mapping=metadata) else: self.metadata = None self.dists = {} self.hidden = hidden if 'dist_type' in kwargs: dist_type = kwargs.pop('dist_type') self.add_distribution(dist_type, **kwargs)
def __init__(self, name, version, deps): self.metadata = Metadata() self.name = name self.version = version self.metadata['Name'] = name self.metadata['Version'] = version self.metadata['Requires-Dist'] = deps
def test_check_name_strict(self): metadata = Metadata() metadata['Version'] = '1.0' metadata['Home-page'] = 'http://pypi.python.org' metadata['Author'] = 'Monty Python' metadata.docutils_support = False self.assertRaises(MetadataMissingError, metadata.check, strict=True)
def test_description_invalid_rst(self): # make sure bad rst is well handled in the description attribute metadata = Metadata() description = ':funkie:`str`' # mimic Sphinx-specific markup metadata['description'] = description missing, warnings = metadata.check(restructuredtext=True) warning = warnings[0][1] self.assertIn('funkie', warning)
def test_check_homepage(self): metadata = Metadata() metadata['Version'] = '1.0' metadata['Name'] = 'vimpdb' metadata['Author'] = 'Monty Python' metadata.docutils_support = False missing, warnings = metadata.check() self.assertEqual(missing, ['Home-page'])
def test_check_author(self): metadata = Metadata() metadata['Version'] = '1.0' metadata['Name'] = 'vimpdb' metadata['Home-page'] = 'http://pypi.python.org' metadata.docutils_support = False missing, warnings = metadata.check() self.assertEqual(missing, ['Author'])
def test_metadata_markers(self): # see if we can be platform-aware PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO') with open(PKG_INFO, 'r', encoding='utf-8') as f: content = f.read() % sys.platform metadata = Metadata(platform_dependent=True) metadata.read_file(StringIO(content)) self.assertEqual(metadata['Requires-Dist'], ['bar']) metadata['Name'] = "baz; sys.platform == 'blah'" # FIXME is None or 'UNKNOWN' correct here? # where is that documented? self.assertEqual(metadata['Name'], None) # test with context context = {'sys.platform': 'okook'} metadata = Metadata(platform_dependent=True, execution_context=context) metadata.read_file(StringIO(content)) self.assertEqual(metadata['Requires-Dist'], ['foo'])
def test_version(self): Metadata(mapping={ 'author': 'xxx', 'name': 'xxx', 'version': 'xxx', 'home_page': 'xxxx' }) logs = self.get_logs() self.assertEqual(1, len(logs)) self.assertIn('not a valid version', logs[0])
def get_metadata(self, project_name, version): """Return the metadatas from the simple index. Currently, download one archive, extract it and use the PKG-INFO file. """ release = self.get_distributions(project_name, version) if not release.metadata: location = release.get_distribution().unpack() pkg_info = os.path.join(location, 'PKG-INFO') release.metadata = Metadata(pkg_info) return release
def test_check_predicates(self): metadata = Metadata() metadata['Version'] = 'rr' metadata['Name'] = 'vimpdb' metadata['Home-page'] = 'http://pypi.python.org' metadata['Author'] = 'Monty Python' metadata['Requires-dist'] = ['Foo (a)'] metadata['Obsoletes-dist'] = ['Foo (a)'] metadata['Provides-dist'] = ['Foo (a)'] missing, warnings = metadata.check() self.assertEqual(len(warnings), 4)
def test_provides_dist(self): fields = { 'name': 'project', 'version': '1.0', 'provides_dist': ['project', 'my.project'] } metadata = Metadata(mapping=fields) self.assertEqual(metadata['Provides-Dist'], ['project', 'my.project']) self.assertEqual(metadata['Metadata-Version'], '1.2', metadata) self.assertNotIn('Requires', metadata) self.assertNotIn('Obsoletes', metadata)
def test_write_metadata(self): # check support of non-ASCII values tmp_dir = self.mkdtemp() my_file = os.path.join(tmp_dir, 'f') metadata = Metadata( mapping={ 'author': 'Mister Café', 'name': 'my.project', 'author': 'Café Junior', 'summary': 'Café torréfié', 'description': 'Héhéhé', 'keywords': ['café', 'coffee'] }) metadata.write(my_file) # the file should use UTF-8 metadata2 = Metadata() with open(my_file, encoding='utf-8') as fp: metadata2.read_file(fp) # XXX when keywords are not defined, metadata will have # 'Keywords': [] but metadata2 will have 'Keywords': [''] # because of a value.split(',') in Metadata.get self.assertEqual(metadata.items(), metadata2.items()) # ASCII also works, it's a subset of UTF-8 metadata = Metadata( mapping={ 'author': 'Mister Cafe', 'name': 'my.project', 'author': 'Cafe Junior', 'summary': 'Cafe torrefie', 'description': 'Hehehe' }) metadata.write(my_file) metadata2 = Metadata() with open(my_file, encoding='utf-8') as fp: metadata2.read_file(fp)
def test_description(self): PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO') with open(PKG_INFO, 'r', encoding='utf-8') as f: content = f.read() % sys.platform metadata = Metadata() metadata.read_file(StringIO(content)) # see if we can read the description now DESC = os.path.join(os.path.dirname(__file__), 'LONG_DESC.txt') with open(DESC) as f: wanted = f.read() self.assertEqual(wanted, metadata['Description']) # save the file somewhere and make sure we can read it back out = StringIO() metadata.write_file(out) out.seek(0) out.seek(0) metadata = Metadata() metadata.read_file(out) self.assertEqual(wanted, metadata['Description'])
def __init__(self, path): if _cache_enabled and path in _cache_path: self.metadata = _cache_path[path].metadata else: metadata_path = os.path.join(path, 'METADATA') self.metadata = Metadata(path=metadata_path) self.name = self.metadata['Name'] self.version = self.metadata['Version'] self.path = path if _cache_enabled and path not in _cache_path: _cache_path[path] = self
def test_instantiation(self): PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO') with open(PKG_INFO, 'r', encoding='utf-8') as f: contents = f.read() fp = StringIO(contents) m = Metadata() self.assertRaises(MetadataUnrecognizedVersionError, m.items) m = Metadata(PKG_INFO) self.assertEqual(len(m.items()), 22) m = Metadata(fileobj=fp) self.assertEqual(len(m.items()), 22) m = Metadata(mapping=dict(name='Test', version='1.0')) self.assertEqual(len(m.items()), 17) d = dict(m.items()) self.assertRaises(TypeError, Metadata, PKG_INFO, fileobj=fp) self.assertRaises(TypeError, Metadata, PKG_INFO, mapping=d) self.assertRaises(TypeError, Metadata, fileobj=fp, mapping=d) self.assertRaises(TypeError, Metadata, PKG_INFO, mapping=m, fileobj=fp)
def test_requires_dist(self): fields = { 'name': 'project', 'version': '1.0', 'requires_dist': ['other', 'another (==1.0)'] } metadata = Metadata(mapping=fields) self.assertEqual(metadata['Requires-Dist'], ['other', 'another (==1.0)']) self.assertEqual(metadata['Metadata-Version'], '1.2') self.assertNotIn('Provides', metadata) self.assertEqual(metadata['Requires-Dist'], ['other', 'another (==1.0)']) self.assertNotIn('Obsoletes', metadata) # make sure write_file uses one RFC 822 header per item fp = StringIO() metadata.write_file(fp) lines = fp.getvalue().split('\n') self.assertIn('Requires-Dist: other', lines) self.assertIn('Requires-Dist: another (==1.0)', lines) # test warnings for invalid version predicates # XXX this would cause no warnings if we used update (or the mapping # argument of the constructor), see comment in Metadata.update metadata = Metadata() metadata['Requires-Dist'] = 'Funky (Groovie)' metadata['Requires-Python'] = '1-4' self.assertEqual(len(self.get_logs()), 2) # test multiple version predicates metadata = Metadata() # XXX check PEP and see if 3 == 3.0 metadata['Requires-Python'] = '>=2.6, <3.0' metadata['Requires-Dist'] = ['Foo (>=2.6, <3.0)'] self.assertEqual(self.get_logs(), [])
def test_description_folding(self): # make sure the indentation is preserved out = StringIO() desc = dedent("""\ example:: We start here and continue here and end here. """) metadata = Metadata() metadata['description'] = desc metadata.write_file(out) folded_desc = desc.replace('\n', '\n' + (7 * ' ') + '|') self.assertIn(folded_desc, out.getvalue())
def test_mapping_api(self): PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO') with open(PKG_INFO, 'r', encoding='utf-8') as f: content = f.read() % sys.platform metadata = Metadata(fileobj=StringIO(content)) self.assertIn('Version', metadata.keys()) self.assertIn('0.5', metadata.values()) self.assertIn(('Version', '0.5'), metadata.items()) metadata.update({'version': '0.6'}) self.assertEqual(metadata['Version'], '0.6') metadata.update([('version', '0.7')]) self.assertEqual(metadata['Version'], '0.7') # make sure update method checks values like the set method does metadata.update({'version': '1--2'}) self.assertEqual(len(self.get_logs()), 1) # XXX caveat: the keys method and friends are not 3.x-style views # should be changed or documented self.assertEqual(list(metadata), metadata.keys())
def __init__(self, attrs=None): """Construct a new Distribution instance: initialize all the attributes of a Distribution, and then use 'attrs' (a dictionary mapping attribute names to values) to assign some of those attributes their "real" values. (Any attributes not mentioned in 'attrs' will be assigned to some null value: 0, None, an empty list or dictionary, etc.) Most importantly, initialize the 'command_obj' attribute to the empty dictionary; this will be filled in with real command objects by 'parse_command_line()'. """ # Default values for our command-line options self.dry_run = False self.help = False for attr in self.display_option_names: setattr(self, attr, False) # Store the configuration self.config = Config(self) # Store the distribution metadata (name, version, author, and so # forth) in a separate object -- we're getting to have enough # information here (and enough command-line options) that it's # worth it. self.metadata = Metadata() # 'cmdclass' maps command names to class objects, so we # can 1) quickly figure out which class to instantiate when # we need to create a new command object, and 2) have a way # for the setup script to override command classes self.cmdclass = {} # 'script_name' and 'script_args' are usually set to sys.argv[0] # and sys.argv[1:], but they can be overridden when the caller is # not necessarily a setup script run from the command line. self.script_name = None self.script_args = None # 'command_options' is where we store command options between # parsing them (from config files, the command line, etc.) and when # they are actually needed -- ie. when the command in question is # instantiated. It is a dictionary of dictionaries of 2-tuples: # command_options = { command_name : { option : (source, value) } } self.command_options = {} # 'dist_files' is the list of (command, pyversion, file) that # have been created by any dist commands run so far. This is # filled regardless of whether the run is dry or not. pyversion # gives sysconfig.get_python_version() if the dist file is # specific to a Python version, 'any' if it is good for all # Python versions on the target platform, and '' for a source # file. pyversion should not be used to specify minimum or # maximum required Python versions; use the metainfo for that # instead. self.dist_files = [] # These options are really the business of various commands, rather # than of the Distribution itself. We provide aliases for them in # Distribution as a convenience to the developer. self.packages = [] self.package_data = {} self.package_dir = None self.py_modules = [] self.libraries = [] self.headers = [] self.ext_modules = [] self.ext_package = None self.include_dirs = [] self.extra_path = None self.scripts = [] self.data_files = {} self.password = '' self.use_2to3 = False self.convert_2to3_doctests = [] self.extra_files = [] # And now initialize bookkeeping stuff that can't be supplied by # the caller at all. 'command_obj' maps command names to # Command instances -- that's how we enforce that every command # class is a singleton. self.command_obj = {} # 'have_run' maps command names to boolean values; it keeps track # of whether we have actually run a particular command, to make it # cheap to "run" a command whenever we think we might need to -- if # it's already been done, no need for expensive filesystem # operations, we just check the 'have_run' dictionary and carry on. # It's only safe to query 'have_run' for a command class that has # been instantiated -- a false value will be inserted when the # command object is created, and replaced with a true value when # the command is successfully run. Thus it's probably best to use # '.get()' rather than a straight lookup. self.have_run = {} # Now we'll use the attrs dictionary (ultimately, keyword args from # the setup script) to possibly override any or all of these # distribution options. if attrs is not None: # Pull out the set of command options and work on them # specifically. Note that this order guarantees that aliased # command options will override any supplied redundantly # through the general options dictionary. options = attrs.get('options') if options is not None: del attrs['options'] for command, cmd_options in options.items(): opt_dict = self.get_option_dict(command) for opt, val in cmd_options.items(): opt_dict[opt] = ("setup script", val) # Now work on the rest of the attributes. Any attribute that's # not already defined is invalid! for key, val in attrs.items(): if self.metadata.is_metadata_field(key): self.metadata[key] = val elif hasattr(self, key): setattr(self, key, val) else: logger.warning( 'unknown argument given to Distribution: %r', key) # no-user-cfg is handled before other command line args # because other args override the config files, and this # one is needed before we can load the config files. # If attrs['script_args'] wasn't passed, assume false. # # This also make sure we just look at the global options self.want_user_cfg = True if self.script_args is not None: for arg in self.script_args: if not arg.startswith('-'): break if arg == '--no-user-cfg': self.want_user_cfg = False break self.finalize_options()
def test_metadata_versions(self): metadata = Metadata(mapping={'name': 'project', 'version': '1.0'}) self.assertEqual(metadata['Metadata-Version'], PKG_INFO_PREFERRED_VERSION) self.assertNotIn('Provides', metadata) self.assertNotIn('Requires', metadata) self.assertNotIn('Obsoletes', metadata) metadata['Classifier'] = ['ok'] metadata.set_metadata_version() self.assertEqual(metadata['Metadata-Version'], '1.1') metadata = Metadata() metadata['Download-URL'] = 'ok' metadata.set_metadata_version() self.assertEqual(metadata['Metadata-Version'], '1.1') metadata = Metadata() metadata['Obsoletes'] = 'ok' metadata.set_metadata_version() self.assertEqual(metadata['Metadata-Version'], '1.1') del metadata['Obsoletes'] metadata['Obsoletes-Dist'] = 'ok' metadata.set_metadata_version() self.assertEqual(metadata['Metadata-Version'], '1.2') metadata.set('Obsoletes', 'ok') self.assertRaises(MetadataConflictError, metadata.set_metadata_version) del metadata['Obsoletes'] del metadata['Obsoletes-Dist'] metadata.set_metadata_version() metadata['Version'] = '1' self.assertEqual(metadata['Metadata-Version'], '1.1') # make sure the _best_version function works okay with # non-conflicting fields from 1.1 and 1.2 (i.e. we want only the # requires/requires-dist and co. pairs to cause a conflict, not all # fields in _314_MARKERS) metadata = Metadata() metadata['Requires-Python'] = '3' metadata['Classifier'] = ['Programming language :: Python :: 3'] metadata.set_metadata_version() self.assertEqual(metadata['Metadata-Version'], '1.2') PKG_INFO = os.path.join(os.path.dirname(__file__), 'SETUPTOOLS-PKG-INFO') metadata = Metadata(PKG_INFO) self.assertEqual(metadata['Metadata-Version'], '1.1') PKG_INFO = os.path.join(os.path.dirname(__file__), 'SETUPTOOLS-PKG-INFO2') metadata = Metadata(PKG_INFO) self.assertEqual(metadata['Metadata-Version'], '1.1') # make sure an empty list for Obsoletes and Requires-dist gets ignored metadata['Obsoletes'] = [] metadata['Requires-dist'] = [] metadata.set_metadata_version() self.assertEqual(metadata['Metadata-Version'], '1.1') # Update the _fields dict directly to prevent 'Metadata-Version' # from being updated by the _set_best_version() method. metadata._fields['Metadata-Version'] = '1.618' self.assertRaises(MetadataUnrecognizedVersionError, metadata.keys)
def set_metadata(self, metadata): if not self.metadata: self.metadata = Metadata() self.metadata.update(metadata)