def accept(self, source): val = source.pop_word(self) if ':' in val: try: (pkgname, vers) = pinfo.parse_package_version_spec(val) return (pkgname, vers) except: raise CommandError('Invalid package name: ' + val) try: pinfo.parse_package_name(val) except: raise CommandError('Invalid package name: ' + val) pkgname = val val = None vers = None if not source.is_empty(): val = source.pop_word(self) try: vers = Version(val) except InvalidVersion: if self.greedy: raise CommandError('Invalid version number: ' + val) if not (val is None): source.push_word(val) return (pkgname, vers)
def accept(self, source): val = source.pop_word(self) try: pinfo.parse_package_name(val) except: raise CommandError('Invalid package name: ' + val) return val
def discover_all_groups(self): """discover_all_groups() -> None Search through the collection directory, and load all the PackageGroups that are available. This only searches the on-disk directory the first time you call it. To force it to re-scan the directory, call clear_cache() first. """ if (self.collection_scanned): return # This is a little tricky. We go through the collection tree, but # we also have to go through the external directory list. (Because # there might be some external packages that do not correspond # to any group in the collection tree.) dirlist = os.listdir(self.collecdir) for key in dirlist: if (key.startswith('.')): continue dirname = os.path.join(self.collecdir, key) # Directories might appear to have upper-case names, but # the group name must be lower-case. (This will fail to load # an upper-case directory on a case-sensitive filesystem. It # will work in a case-preserving filesystem.) key = key.lower() if (not os.path.isdir(dirname)): continue try: parse_package_name(key) self.load_group(key) except: continue # Go through the external directory list. If the external package # is not already in a loaded group, load its group. (The load_group # method is smart enough to find it, even though there is no # matching directory in the collection tree.) for (name, vers) in self.external_dirs.keys(): if (not self.package_groups.has_key(name)): try: self.load_group(name) except: continue # Now we never need to do this again. (Until the cache is cleared.) self.collection_scanned = True
def accept(self, source): val = source.pop_word(self) if ':' in val and not (val.startswith('/') or val.startswith('\\') or val.startswith('.')): pos = val.find(':') dotpos = val.find('.') if dotpos < 0 or dotpos > pos: return (collect.Source_URL, val) if val.endswith('.zip') or val.endswith(collect.Suffix_PackageArchive): return (collect.Source_FILE, val) if ':' in val: try: (pkgname, vers) = pinfo.parse_package_version_spec(val) return (collect.Source_PACKAGE, (pkgname, vers)) except: pass try: pinfo.parse_package_name(val) except: return (collect.Source_FILE, val) pkgname = val val = None vers = None if not source.is_empty(): val = source.pop_word(self) try: vers = Version(val) except InvalidVersion: if self.greedy: raise CommandError('Invalid version number: ' + val) if not (val is None): source.push_word(val) return (collect.Source_PACKAGE, (pkgname, vers))
def test_parse_package_name(self): valid_list = [ ('hello', ['hello']), ('h.e.l.l.o', ['h', 'e', 'l', 'l', 'o']), ('x_00t_99', ['x_00t_99']), ('t0.t1', ['t0', 't1']), ('_._005_.a', ['_', '_005_', 'a']), ] invalid_list = [ '', ' ', '.', 'a.', '.b', '.c.', 'd..e', '..', 'x ', ' y', 'a b', 'a. b', 'a .b', '0', '0x', 'a.0', 'x.y.9z', 'A', 'Hello', 'hello.There', 'olleH', 'a,b', 'a-b', 'a+b', 'a/b', 'a\\b', ] for (name, result) in valid_list: res = parse_package_name(name) self.assertEqual(res, result) for name in invalid_list: self.assertRaises(ValueError, parse_package_name, name)
def add_external_package(self, dirname, metadata=None, resources=None): """add_external_package(dirname, metadata=None, resources=None) -> (str, VersionNumber) Add an external directory to load a package from. The argument must be a complete (unpacked) Boodler directory, with a Metadata file. That package will then be loadable (possibly hiding a package in the collection directory). The metadata and resources fields, if supplied, override the Metadata and Resources files in the directory. (The files do not even have to exist.) If the metadata and resources fields are None, the directory's files are checked as usual. (This override feature is used by the package creation tool. It should not be used for any other purpose.) Returns the name and version of the package that was found. Since this changes the set of what packages are available, it implicitly invokes clear_cache(). The external package facility is not intended to be how most packages are loaded. Most packages should be in the collection directory. You might add an external package in your development workspace (while developing a soundscape), or in a temporary download directory (while deciding whether to install a newly- downloaded package). """ label = '<external '+dirname+'>' exrec = ExternalDir(dirname, metadata, resources) if (not os.path.isdir(dirname)): raise PackageLoadError(label, 'not a directory') # Load the metadata file, and see what package/version we are # dealing with. (But only if a metadata object wasn't handed to # us!) if (not metadata): metadatafile = os.path.join(dirname, Filename_Metadata) if (not os.path.isfile(metadatafile)): raise PackageLoadError(label, 'package has no metadata file') fl = open(metadatafile, 'rbU') try: metadata = boopak.pinfo.Metadata(label, fl) finally: fl.close() val = metadata.get_one('boodler.package') if (not val): raise PackageLoadError(label, 'no boodler.package metadata entry') parse_package_name(val) pkgname = str(val) val = metadata.get_one('boodler.version') if (not val): vers = version.VersionNumber() else: vers = version.VersionNumber(val) # Add this to the external directory table. (We have not actually # loaded the package.) self.external_dirs[(pkgname, vers)] = exrec self.clear_cache() return (pkgname, vers)
def load_specific(self, pkgname, vers): """load_specific(pkgname, vers) -> PackageInfo Load a package, given its name and a specific version number. (The version number may be a VersionNumber or a string that can be converted to one.) This is an internal call; external callers should use load(). (load_specific() is intended to be called by a package function which has already consulted package_groups and a PackageGroup object. If you bypass those, you might load a package which is not part of any PackageGroup. That would leave the cache in a confusing state.) """ if (type(vers) in [str, unicode]): vers = version.VersionNumber(vers) pkg = self.packages.get( (pkgname, vers) ) if (pkg): return pkg # Make sure the name is valid. parse_package_name(pkgname) pkgname = str(pkgname) exrec = self.external_dirs.get( (pkgname, vers) ) if (exrec is None): is_external = False dirname = self.generate_package_path(pkgname, vers) else: is_external = True dirname = exrec.dirname # dirname is now the directory where the package should reside. # This may be in the collection or external. if (not os.path.isdir(dirname)): raise PackageNotFoundError(pkgname, 'package version directory does not exist') # Read the metadata. (But if the external directory record has # an overriding Metadata object, use that instead.) if (is_external and exrec.metadata): metadata = exrec.metadata else: metadatafile = os.path.join(dirname, Filename_Metadata) if (not os.path.isfile(metadatafile)): raise PackageLoadError(pkgname, 'package has no metadata file') fl = open(metadatafile, 'rbU') try: metadata = boopak.pinfo.Metadata(pkgname, fl) finally: fl.close() # Read the resources (if there are any). Again, there may be an # overriding Resources object. if (is_external and exrec.resources): resources = exrec.resources else: resourcesfile = os.path.join(dirname, Filename_Resources) if (os.path.isfile(resourcesfile)): fl = open(resourcesfile, 'rbU') try: resources = boopak.pinfo.Resources(pkgname, fl) finally: fl.close() else: # Create an empty resources object resources = boopak.pinfo.Resources(pkgname) # Create the PackageInfo object and look through its metadata. pkg = PackageInfo(self, pkgname, vers, dirname, metadata, resources, is_external) pkg.validate_metadata() # Valid; we can now add it to the cache. self.packages[(pkgname, vers)] = pkg self.package_names[pkg.encoded_name] = pkg return pkg
def load_group(self, pkgname): """load_group(self, pkgname) -> PackageGroup Load a PackageGroup, given its package name. This is not tremendously useful for outside users, although you can call it if you want. A PackageGroup represents all the available versions of a particular package. """ pgroup = self.package_groups.get(pkgname) if (pgroup): return pgroup # Make sure the name is valid. parse_package_name(pkgname) pkgname = str(pkgname) dirname = None versionfile = None # We determine the list of available versions by listing the # package directory in the collection tree. But we have to include # any versions that come in through external directories, too. # Create a list of external versions. external_versions = [] for (name, vers) in self.external_dirs.keys(): if (pkgname == name): external_versions.append(vers) # Find the package directory. (But it's possible that there *is* # no package directory. That's an error, unless we have external # versions -- in which case we keep trucking forward with the # dirname set to None.) dirname = self.generate_package_path(pkgname) if (not os.path.isdir(dirname)): if (not external_versions): raise PackageNotFoundError(pkgname, 'package directory does not exist') dirname = None # Open the Versions file in the package directory. (If we have # a package directory, and a Versions file.) if (dirname): versionfile = os.path.join(dirname, Filename_Versions) if (not os.path.isfile(versionfile)): # We don't raise an exception here, because the install # command needs to tolerate a screwy Collection long # enough to fix it. versionfile = None # Create the PackageGroup object itself. pgroup = PackageGroup(self, pkgname, dirname) fl = None try: if (dirname and versionfile): fl = open(versionfile, 'rbU') # Go through the Versions file and the external versions list # (either of which may be nonexistent). pgroup.discover_versions(fl, external_versions) finally: if (not (fl is None)): fl.close() # Add the new group to the cache. self.package_groups[pkgname] = pgroup return pgroup