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)
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)
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))
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)
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
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)))
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)
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)
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
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)
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