Example #1
0
def parse_package_version_spec(val):
    """parse_package_version_spec(val) -> (pkgname, VersionNumber)
        or (pkgname, VersionSpec) or (pkgname, None)

    Parse a package identifier together with its version spec 
    (e.g., "org.boodler.sample:1.0") or exact version spec 
    (e.g., "org.boodler.sample::1.0"). If neither is present,
    the second value of the return tuple will be None.
    
    Raises a ValueError if the name was in any way invalid. (Thus,
    this function can be used to ensure that a package name is valid.)
    """

    vers = None

    pos = val.find(':')
    if (pos >= 0):
        spec = val[pos + 1:]
        val = val[:pos]
        if (spec.startswith(':')):
            vers = version.VersionNumber(spec[1:])
        else:
            vers = version.VersionSpec(spec)

    parse_package_name(val)

    return (val, vers)
Example #2
0
    def subtest_dependency_cases(self):
        pkg = self.loader.load('depend.on.cases')
        tup = pkg.load_dependencies()

        badls = [
                ('missing.nospec', None),
                ('missing.spec', version.VersionSpec('2.0')),
                ('missing.num', version.VersionNumber('3.0')),
            ]
        badls.sort()

        baddict = dict([ (key,[val]) for (key,val) in badls ])
        
        self.assertEqual(tup, 
            (self.packages_to_set([
                ('depend.on.cases', '1.0'),
                ('depend.one', '2.0'),
            ]),
            baddict,
            0))

        bad = self.loader.find_all_dependencies()[2]
        ls = bad[('depend.on.cases', version.VersionNumber('1.0'))]
        ls.sort()
        self.assertEqual(ls, badls)
Example #3
0
    def subtest_load_group(self):
        grp = self.loader.load_group('version.specs')

        ls = grp.get_versions()
        ls.sort()
        ls = [ str(val) for val in ls ]
        self.assertEqual(ls, ['1.2', '1.5', '1.5.3', '2.5'])

        self.assertEqual(grp.get_num_versions(), 4)
        
        vers = grp.find_version_match()
        self.assertEqual(vers, '2.5')
        vers = grp.find_version_match(version.VersionSpec('1'))
        self.assertEqual(vers, '1.5.3')
Example #4
0
    def load(self, pkgname, versionspec=None):
        """load(pkgname, versionspec=None) -> PackageInfo

        Load a package, given its name and a version spec.

        If no second argument is given, the most recent available version
        of the package is loaded. If the argument is a VersionNumber,
        that version will be loaded. If it is a VersionSpec, the most
        recent version that matches the spec will be loaded. A string
        value will be converted to a VersionSpec (not a VersionNumber).

        This generates a PackageNotFoundError if no matching package
        is available. It generates PackageLoadError if the package
        was malformed in some way which prevented loading.
        """
        
        if (type(versionspec) in [str, unicode]):
            versionspec = version.VersionSpec(versionspec)
            
        pgroup = self.load_group(pkgname)
        if (not pgroup.get_num_versions()):
            raise PackageNotFoundError(pkgname,
                'no versions available')

        if (isinstance(versionspec, version.VersionNumber)):
            if (not pgroup.has_version(versionspec)):
                raise PackageNotFoundError(pkgname,
                    'version \'' + str(versionspec) + '\' not available')
            vers = versionspec
        elif ((versionspec is None)
            or isinstance(versionspec, version.VersionSpec)):
            vers = pgroup.find_version_match(versionspec)
        else:
            raise PackageLoadError(pkgname,
                'load spec must be a string, VersionSpec, or VersionNumber')

        if (not vers):
            raise PackageNotFoundError(pkgname,
                'no version matching \'' + str(versionspec) + '\'')
        
        pkg = self.load_specific(pkgname, vers)
        if (self.currently_importing):
            # Record what package imported this one, and with what spec
            self.currently_importing.imported_pkg_specs[pkgname] = versionspec
        return pkg
Example #5
0
    def test_parse_package_version_spec(self):
        valid_list = [
            'foo:1.0',
            'foo:2-',
            'foo:-3.1',
            'foo:2-4',
            'foo::1.2.3',
            'foo.bar::1.5.6a',
        ]
        invalid_list = [
            '0x',
            ':foo',
            'foo:0.1',
            'foo:1.2.3',
            'foo::0.1',
            'foo:::1.0',
            'foo:1:2',
            'foo: 1.0',
            'foo :1.0',
            'foo: :1.0',
            'foo:: 1.0',
        ]

        (pkg, vers) = parse_package_version_spec('x.y.z')
        self.assertEqual(pkg, 'x.y.z')
        self.assertEqual(vers, None)

        (pkg, vers) = parse_package_version_spec('x.y.z:2.4')
        self.assertEqual(pkg, 'x.y.z')
        self.assert_(isinstance(vers, version.VersionSpec))
        self.assertEqual(vers, version.VersionSpec('2.4'))

        (pkg, vers) = parse_package_version_spec('x.y.z::2.4.6')
        self.assertEqual(pkg, 'x.y.z')
        self.assert_(isinstance(vers, version.VersionNumber))
        self.assertEqual(vers, version.VersionNumber('2.4.6'))

        for val in valid_list:
            parse_package_version_spec(val)
        for val in invalid_list:
            self.assertRaises(ValueError, parse_package_version_spec, val)
Example #6
0
    def record_import(self, pkg, name, spec=None):
        """record_import(pkg, name, spec=None) -> None

        Note a bimport() call by a package being imported. This should
        only be called by bimport().
        """

        if (type(spec) in [str, unicode]):
            spec = version.VersionSpec(spec)

        if (isinstance(spec, version.VersionNumber)):
            isexact = True
        elif ((spec is None) or isinstance(spec, version.VersionSpec)):
            # ok
            isexact = False
        else:
            return

        ls = self.import_recorder.get(pkg.key)
        if (not ls):
            ls = []
            self.import_recorder[pkg.key] = ls

        ls.append((name, spec))
Example #7
0
    def load_item_by_name(self, name, package=None):
        """load_item_by_name(name, package=None) -> value

        Given a string that names a resource -- for example,
        'com.eblong.example/reptile.Hiss' -- import the module and
        return the resource object.

        If the string begins with a slash ('/boodle.builtin.NullAgent')
        then the regular Python modules are searched. No importing
        is done in this case; it is really intended only for the
        contents of boodle.agent.

        If the string ends with a slash ('com.eblong.example/'), then
        the module itself is returned.

        If the package argument is supplied (a PackageInfo object), it
        becomes the package to look in for unqualified resource names
        ('reptile.Hiss'). If no package argument is supplied, then
        an unqualified resource name raises ValueError.
        """

        pos = name.find('/')
        if (pos < 0):
            if (package is None):
                raise ValueError('argument must be of the form package/Resource')
            mod = package.get_content()
        elif (pos == 0):
            # consult Python's module map
            name = name[ 1 : ]
            headtail = name.split('.', 1)
            if (len(headtail) != 2):
                raise ValueError('argument must be of the form package/Resource')
            (modname, name) = headtail
            mod = sys.modules.get(modname)
            if (mod is None):
                raise ValueError('not found in Python modules: ' + modname)
        else:
            pkgname = name[ : pos ]
            name = name[ pos+1 : ]
            
            pkgspec = None
            pos = pkgname.find(':')
            if (pos >= 0):
                val = pkgname[ pos+1 : ]
                pkgname = pkgname[ : pos ]
                if (val.startswith(':')):
                    val = val[ 1 : ]
                    pkgspec = version.VersionNumber(val)
                else:
                    pkgspec = version.VersionSpec(val)

            package = self.load(pkgname, pkgspec)
            mod = package.get_content()

        if (not name):
            # "module/" returns the module itself
            return mod
        
        namels = name.split('.')
        try:
            res = mod
            for el in namels:
                res = getattr(res, el)
            return res
        except AttributeError, ex:
            raise ValueError('unable to load ' + name + ' (' + str(ex) + ')')
Example #8
0
    def setUp(self):
        basedir = tempfile.mkdtemp(prefix='test_pload')
        self.basedir = basedir
        coldir = os.path.join(basedir, 'Collection')
        os.makedirs(coldir)
        self.coldir = coldir

        self.loader = pload.PackageLoader(coldir, importing_ok=True)

        build_package(self.loader, 'simple.test', '1.0')
        
        build_package(self.loader, 'import.module',
            mod_content={'key':'val'}, mod_main='main')
        build_package(self.loader, 'import.module', '2.0',
            mod_content={'key':'val2'}, mod_main='__init__')
            
        build_package(self.loader, 'version.specs', '1.2')
        build_package(self.loader, 'version.specs', '1.5')
        build_package(self.loader, 'version.specs', '1.5.3')
        build_package(self.loader, 'version.specs', '2.5')

        self.empty_dir_path = os.path.join(basedir, 'empty-dir')
        os.mkdir(self.empty_dir_path)

        self.external_one_path = os.path.join(basedir, 'external-one')
        build_package(self.loader, 'external.one', '1.0',
            external_path=self.external_one_path,
            metadata={'key':'ext1'})
            
        build_package(self.loader, 'external.two', '2.5',
            mod_content={'key':'orig'})
        self.external_two_path = os.path.join(basedir, 'external-two')
        build_package(self.loader, 'external.two', '2.5',
            external_path=self.external_two_path,
            mod_content={'key':'replacement'})
            
        build_package(self.loader, 'external.three', '2.0',
            mod_content={'key':'orig'})
        self.external_three_path = os.path.join(basedir, 'external-three')
        build_package(self.loader, 'external.three', '2.5',
            external_path=self.external_three_path,
            mod_content={'key':'replacement'})

        build_package(self.loader, 'only.files',
            resources={
                'one':'one.txt',
                'dir.two':'dir/two.txt',
                'dir.three':'dir/three.txt',
            })

        build_package(self.loader, 'mod.and.files',
            mod_content=[
                'from boopak import package',
                'package.bexport("one")',
                'package.bexport("dir")',
                'package.bexport("alt.four")',
            ],
            resources={
                'zero':'zero.txt',
                'one':'one.txt',
                'dir.two':'dir/two.txt',
                'dir.three':'dir/three.txt',
                'alt.four':'alt/four.txt',
                'alt.five':'alt/five.txt',
            })

        build_package(self.loader, 'depend.one', '1',
            mod_content={'key':11})
        build_package(self.loader, 'depend.one', '2',
            mod_content={'key':12})
        build_package(self.loader, 'depend.two',
            depends=('depend.one', '1'),
            mod_content={'key':21})
        build_package(self.loader, 'depend.two', '2',
            depends=('depend.one', '2'),
            mod_content={'key':22})
        build_package(self.loader, 'depend.three',
            depends=('depend.two', None),
            mod_content={'key':3})
        
        build_package(self.loader, 'self.depend', '1',
            depends=('self.depend', '1'),
            mod_content={'key':1})
        build_package(self.loader, 'self.depend', '2',
            depends=('self.depend', '1'),
            mod_content={'key':2})
            
        build_package(self.loader, 'mutual.depend.one',
            depends=('mutual.depend.two', None),
            mod_content={'key':1})
        build_package(self.loader, 'mutual.depend.two',
            depends=('mutual.depend.one', None),
            mod_content={'key':2})
            
        build_package(self.loader, 'depend.on.fail',
            depends=('external.one', None),
            mod_content={'key':1})

        build_package(self.loader, 'depend.on.cases',
            depends=[
                ('depend.one', None),
                ('missing.nospec', None),
                ('missing.spec', version.VersionSpec('2')),
                ('missing.num', version.VersionNumber('3')),
            ])

        build_package(self.loader, 'unicode.metadata',
            metadata={
                'test.plain':'result',
                'test.unicode':'alpha is \xce\xb1',
            })
            
        build_package(self.loader, 'mixin.static',
            resources={
                'zero':'zero.txt',
                'one':'one.txt',
                'two':'two.txt',
                'mixer': { 'dc.title':'Mix-In' },
            },
            mod_content=[
                'from boopak.package import bexport',
                'bexport()',
                'from boodle.sample import MixIn',
                'class mixer(MixIn):',
                '  ranges = [',
                '    MixIn.range(2.0, 2.1, two, volume=1.4),',
                '    MixIn.range(1.5, one, pitch=1.3),',
                '    MixIn.range(1.0, zero),',
                '  ]',
                '  default = MixIn.default(zero)',
            ])
            
        build_package(self.loader, 'mixin.file',
            resources={
                'zero':'zero.txt',
                'one':'one.txt',
                'two':'two.txt',
                'mixer':('mixer.mixin', """
# Comment.
range 2 2.1 two - 1.4
range - 1.5 one 1.3
range - 1.0 zero
else two
"""),
            })
Example #9
0
    def validate_metadata(self):
        """validate_metadata() -> None

        Make sure that the metadata object attached to this package
        correctly describes the package. Also loads up various fields
        with information from the metadata object.

        Also checks that the resource tree has a valid shape.

        If anything is discovered to be wrong, this raises PackageLoadError.

        This is called by the package loader (and nothing else should
        call it).
        """

        pkgname = self.name
        metadata = self.metadata

        val = metadata.get_one('boodler.package')
        if (not val):
            raise PackageLoadError(pkgname,
                                   'no boodler.package metadata entry')
        if (val != pkgname):
            raise PackageLoadError(
                pkgname,
                'boodler.package does not match package location: ' + val)

        val = metadata.get_one('boodler.version')
        if (not val):
            val = '(missing, 1.0 assumed)'
            vers = version.VersionNumber()
        else:
            vers = version.VersionNumber(val)
        if (vers != self.version):
            raise PackageLoadError(
                pkgname,
                'boodler.version does not match package version: ' + val)

        val = metadata.get_one('boodler.main')
        if (not val):
            pass
        elif (val == '.'):
            pass
        elif (ident_name_regexp.match(val)):
            pass
        else:
            raise PackageLoadError(pkgname,
                                   'boodler.main is not a module or . :' + val)

        val = metadata.get_one('boodler.api_required')
        if (val):
            spec = version.VersionSpec(val)
            if (self.loader.boodler_api_vers):
                if (not spec.match(self.loader.boodler_api_vers)):
                    raise PackageLoadError(
                        pkgname,
                        'boodler.api_required does not match Boodler version: '
                        + val)

        for val in metadata.get_all('boodler.requires'):
            try:
                pos = val.find(' ')
                if (pos < 0):
                    deppkg = val
                    depspec = None
                else:
                    deppkg = val[:pos].strip()
                    depspec = val[pos + 1:].strip()
                    depspec = version.VersionSpec(depspec)
                parse_package_name(deppkg)
                deppkg = str(deppkg)
                self.dependencies.add((deppkg, depspec))
            except ValueError, ex:
                raise PackageLoadError(pkgname,
                                       'boodler.requires line invalid: ' + val)