Ejemplo n.º 1
0
 def merge_sources(self, sources):
     """ Record the fact that we saw this definition in 'sources' """
     for entry in sources:
         if not self.value and entry.value:
             self.value = entry.value
             trace("[-- %s] %s=%s" % (entry.source, self.key, entry.value))
         self.sources.append(entry)
Ejemplo n.º 2
0
 def get_requirements(*relative_paths):
     """ Read old-school requirements.txt type file """
     for path in relative_paths:
         path = project_path(path)
         if os.path.isfile(path):
             trace("found requirements: %s" % path)
             return RequirementsEntry(path)
Ejemplo n.º 3
0
    def __init__(self, meta, scm):
        """
        :param setupmeta.model.SetupMeta meta: Parent meta object
        :param Scm scm: Backend SCM
        """
        self.meta = meta
        given = meta.value("versioning")
        self.strategy = Strategy.from_meta(given)
        self.enabled = bool(given and self.strategy
                            and not self.strategy.problem)
        self.scm = scm
        self.generate_version_file = scm and scm.root != setupmeta.MetaDefs.project_dir and not os.environ.get(
            setupmeta.SCM_DESCRIBE)
        self.problem = None
        if not self.strategy:
            self.problem = "setupmeta versioning not enabled"

        elif self.strategy.problem:
            self.problem = self.strategy.problem

        elif not self.scm:
            self.problem = "project not under a supported SCM"

        setupmeta.trace(
            "versioning given: '%s', strategy: [%s], problem: [%s]" %
            (given, self.strategy, self.problem))
Ejemplo n.º 4
0
    def auto_fill_version(self):
        """
        Auto-fill version as defined by self.strategy
        :param setupmeta.model.SetupMeta meta: Parent meta object
        """
        pygradle_version = os.environ.get("PYGRADLE_PROJECT_VERSION")
        if pygradle_version:
            # Minimal support for https://github.com/linkedin/pygradle
            self.meta.auto_fill("version",
                                pygradle_version,
                                "pygradle",
                                override=True)
            return

        if not self.enabled:
            setupmeta.trace("not auto-filling version, versioning is disabled")
            return

        vdef = self.meta.definitions.get("version")
        if vdef and vdef.source and vdef.source.lower().endswith("-info"):
            # We already got version from PKG-INFO
            return

        cv = vdef.sources[0].value if vdef and vdef.sources else None
        if self.problem:
            if not cv:
                self.meta.auto_fill("version", "0.0.0", "missing")
            if self.strategy:
                setupmeta.warn(self.problem)
            setupmeta.trace("not auto-filling version due to problem: [%s]" %
                            self.problem)
            return

        gv = self.scm.get_version()
        if self.generate_version_file:
            path = setupmeta.project_path(setupmeta.VERSION_FILE)
            with open(path, "w") as fh:
                fh.write("%s" % gv)

        if gv.patch and "patch" not in self.strategy.bumpable:
            msg = "patch version component should be .0 for versioning strategy '%s', " % self.strategy
            msg += "'.%s' from current version tag '%s' will be ignored" % (
                gv.patch, gv)
            setupmeta.warn(msg)

        rendered = self.strategy.rendered(gv)
        if cv and gv:
            cvv = Version(main=cv,
                          distance=gv.distance,
                          commitid=gv.commitid,
                          dirty=gv.dirty)
            if cvv.major != gv.major or cvv.minor != gv.minor or cvv.patch != gv.patch:
                source = vdef.sources[0].source
                expected = rendered[:len(cv)]
                msg = "In %s version should be %s, not %s" % (source, expected,
                                                              cv)
                setupmeta.warn(msg)

        self.meta.auto_fill("version", rendered, self.scm.name, override=True)
Ejemplo n.º 5
0
def project_scm(root):
    """
    :param str root: Path to project folder
    :return setupmeta.scm.Scm: SCM used by project, if any
    """
    scm_root = find_scm_root(os.path.abspath(root), 'git')
    if scm_root:
        return Git(scm_root)
    if os.environ.get(setupmeta.SCM_DESCRIBE):
        return Snapshot(root)
    snapshot = os.path.join(root, setupmeta.VERSION_FILE)
    if os.path.isfile(snapshot):
        return Snapshot(root)
    setupmeta.trace("could not determine SCM for '%s'" % root)
    return None
Ejemplo n.º 6
0
 def add(self, value, source, override=False):
     """
     :param value: Value to add (first value wins, unless override used)
     :param str source: Where this key/value came from
     :param bool override: If True, 'value' is forcibly taken
     """
     if isinstance(source, list):
         return self.merge_sources(source)
     if override or not self.value:
         self.value = value
     entry = DefinitionEntry(self.key, value, source)
     if override:
         self.sources.insert(0, entry)
         trace("[high: %s] %s=%s" % (source, self.key, short(value)))
     else:
         self.sources.append(entry)
         trace("[low: %s] %s=%s" % (source, self.key, short(value)))
Ejemplo n.º 7
0
    def __init__(self, root):
        self.path = os.path.join(root, "PKG-INFO")
        self.info = {}
        self.name = None
        self.dependency_links = None
        self.entry_points_txt = None
        self.requires_txt = None
        lines = readlines(self.path)
        if not lines:
            return

        # Parse PKG-INFO when present
        key = None
        for line_number, line in enumerate(lines, start=1):
            m = RE_PKG_KEY_VALUE.match(line)
            if m:
                key = m.group(1).lower().replace("-", "_")
                key = self._canonical_names.get(key, key)
                if key not in MetaDefs.all_fields:
                    continue

                value = m.group(2)
                if key in self._list_types:
                    if key not in self.info:
                        self.info[key] = []

                    self.info[key].append(value)

                else:
                    self.info[key] = value

            elif key in self._list_types:
                # Indented description applying to previous key
                self.info[key].append(line[8:].rstrip())

            elif line.strip():
                trace("Unknown format line %s in %s: %s" %
                      (line_number, self.path, line))

        self.name = self.info.get("name")
        self.pythonified_name = pythonified_name(self.name)
        self.info["long_description"] = "\n".join(
            self.info.get("long_description", []))
        self.load_more_info(root)
Ejemplo n.º 8
0
    def auto_fill_version(self):
        """
        Auto-fill version as defined by self.strategy
        :param setupmeta.model.SetupMeta meta: Parent meta object
        """
        pygradle_version = os.environ.get('PYGRADLE_PROJECT_VERSION')
        if pygradle_version:
            # Minimal support for https://github.com/linkedin/pygradle
            self.meta.auto_fill('version',
                                pygradle_version,
                                'pygradle',
                                override=True)
            return

        if not self.enabled:
            setupmeta.trace("not auto-filling version, versioning is disabled")
            return

        vdef = self.meta.definitions.get('version')
        cv = vdef.sources[0].value if vdef and vdef.sources else None
        if self.problem:
            if not cv:
                self.meta.auto_fill('version', '0.0.0', 'missing')
            if self.strategy:
                warnings.warn(self.problem)
            setupmeta.trace("not auto-filling version due to problem: [%s]" %
                            self.problem)
            return

        gv = self.scm.get_version()
        if self.generate_version_file:
            path = setupmeta.project_path(setupmeta.VERSION_FILE)
            with open(path, 'w') as fh:
                fh.write("%s" % gv)

        rendered = self.strategy.rendered(gv)
        if cv and rendered and not rendered.startswith(cv):
            source = vdef.sources[0].source
            expected = rendered[:len(cv)]
            msg = "In %s version should be %s, not %s" % (source, expected, cv)
            warnings.warn(msg)
        self.meta.auto_fill('version', rendered, self.scm.name, override=True)
Ejemplo n.º 9
0
def find_packages(name, subfolder=None):
    """ Find packages for 'name' (if any), 'subfolder' is like "src" """
    result = set()
    if subfolder:
        path = project_path(subfolder, name)
        trace("looking for packages in '%s/%s'" % (subfolder, name))
    else:
        path = project_path(name)
        trace("looking for packages in '%s'" % name)
    init_py = os.path.join(path, '__init__.py')
    if os.path.isfile(init_py):
        result.add(name)
        trace("found package '%s'" % name)
        for subpackage in setuptools.find_packages(where=path):
            result.add("%s.%s" % (name, subpackage))
            trace("found subpackage '%s.%s'" % (name, subpackage))
    return result
Ejemplo n.º 10
0
    def find_project_dir(self, setup_py_path):
        """
        :param str|None setup_py_path: Given setup.py (when invoked from test)
        """
        if not setup_py_path:
            # Determine path to setup.py module from call stack
            for frame in inspect.stack():
                module = inspect.getmodule(frame[0])
                if module and is_setup_py_path(module.__file__):
                    setup_py_path = module.__file__
                    trace("setup.py found from call stack: %s" % setup_py_path)
                    break

        if not setup_py_path and sys.argv:
            if is_setup_py_path(sys.argv[0]):
                setup_py_path = sys.argv[0]
                trace("setup.py found from sys.argv: %s" % setup_py_path)

        if is_setup_py_path(setup_py_path):
            setup_py_path = os.path.abspath(setup_py_path)
            MetaDefs.project_dir = os.path.dirname(setup_py_path)
            trace("project dir: %s" % MetaDefs.project_dir)
Ejemplo n.º 11
0
    def finalize(self, upstream):
        self.attrs.update(MetaDefs.dist_to_dict(upstream))

        self.find_project_dir(self.attrs.pop("_setup_py_path", None))
        scm = self.attrs.pop("scm", None)

        # Add definitions from setup()'s attrs (highest priority)
        for key, value in self.attrs.items():
            if key not in self.definitions:
                self.add_definition(key, value, EXPLICIT)

        # Add definitions from PKG-INFO, when available
        self.pkg_info = PackageInfo(MetaDefs.project_dir)
        for key, value in self.pkg_info.info.items():
            if key in MetaDefs.all_fields:
                self.add_definition(key, value,
                                    relative_path(self.pkg_info.path))

        # Allow to auto-fill 'name' from setup.py's __title__, if any
        self.merge(SimpleModule("setup.py"))
        title = self.definitions.get("title")
        if title:
            self.auto_fill("name", title.value, source=title.source)

        if "--name" in sys.argv[1:3]:
            # No need to waste time auto-filling anything if all we need to show is package name
            return self

        packages = self.attrs.get("packages", [])
        py_modules = self.attrs.get("py_modules", [])

        if not packages and not py_modules and self.name:
            # Try to auto-determine a good default from 'self.name'
            name = self.pythonified_name
            src_folder = project_path("src")
            if os.path.isdir(src_folder):
                trace("looking for src packages in %s" % src_folder)
                packages = setuptools.find_packages(where=src_folder)
                if not packages and os.path.isfile(
                        project_path("src", "%s.py" % name)):
                    py_modules = [name]

                if packages or py_modules:
                    self.auto_fill("package_dir", {"": "src"})

            else:
                src_folder = project_path()
                if os.path.isdir(src_folder):
                    trace("looking for direct packages in %s" % src_folder)
                    with current_folder(src_folder):
                        raw_packages = setuptools.find_packages()
                        if raw_packages:
                            # Keep only packages that start with the expected name
                            # For any other use-case, user must explicitly list their packages
                            packages = [
                                p for p in raw_packages if p.startswith(name)
                            ]
                            if packages != raw_packages:
                                trace("all packages found: %s" % raw_packages)

                if not packages and os.path.isfile(project_path(
                        "%s.py" % name)):
                    py_modules = [name]

            if packages:
                self.auto_fill("packages", sorted(packages))

            if py_modules:
                self.auto_fill("py_modules", py_modules)

        # Scan the usual/conventional places
        for py_module in py_modules:
            self.merge(SimpleModule("%s.py" % py_module))

        for package in packages:
            if package and "." not in package:
                # Look at top level modules only
                self.merge(
                    SimpleModule(package, "__about__.py"),
                    SimpleModule(package, "__version__.py"),
                    SimpleModule(package, "__init__.py"),
                    SimpleModule("src", package, "__about__.py"),
                    SimpleModule("src", package, "__version__.py"),
                    SimpleModule("src", package, "__init__.py"),
                )

        if not self.name:
            warn(
                "'name' not specified in setup.py, auto-fill will be incomplete"
            )

        elif not self.definitions.get("packages") and not self.definitions.get(
                "py_modules"):
            warn(
                "No 'packages' or 'py_modules' defined, this is an empty python package"
            )

        scm = scm or project_scm(MetaDefs.project_dir)
        self.versioning = Versioning(self, scm)
        self.versioning.auto_fill_version()

        self.fill_urls()

        self.auto_adjust("author", self.extract_email)
        self.auto_adjust("contact", self.extract_email)
        self.auto_adjust("maintainer", self.extract_email)

        self.requirements = Requirements(self.pkg_info)
        self.auto_fill_requires("install_requires")
        self.auto_fill_entry_points()
        self.auto_fill_license()
        self.auto_fill_long_description()
        self.auto_fill_include_package_data()

        return self