From 24ea030b01f7b39952c8fe6c19620f695e52886d Mon Sep 17 00:00:00 2001 From: Will Ayd Date: Wed, 20 Mar 2024 18:42:12 -0400 Subject: [PATCH 01/17] Use NumPy methodology for generating version --- generate_version.py | 69 --- meson.build | 23 +- pandas/__init__.py | 17 +- pandas/_build_utils/gitversion.py | 90 ++++ pandas/_version.py | 692 ------------------------------ pandas/meson.build | 28 +- pyproject.toml | 4 +- 7 files changed, 122 insertions(+), 801 deletions(-) delete mode 100644 generate_version.py create mode 100755 pandas/_build_utils/gitversion.py delete mode 100644 pandas/_version.py diff --git a/generate_version.py b/generate_version.py deleted file mode 100644 index 7dc1bdcf329d0..0000000000000 --- a/generate_version.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env python3 - -# Note: This file has to live next to setup.py or versioneer will not work -import argparse -import os -import sys - -import versioneer - -sys.path.insert(0, "") - - -def write_version_info(path) -> None: - version = None - git_version = None - - try: - import _version_meson - - version = _version_meson.__version__ - git_version = _version_meson.__git_version__ - except ImportError: - version = versioneer.get_version() - git_version = versioneer.get_versions()["full-revisionid"] - if os.environ.get("MESON_DIST_ROOT"): - path = os.path.join(os.environ.get("MESON_DIST_ROOT"), path) - with open(path, "w", encoding="utf-8") as file: - file.write(f'__version__="{version}"\n') - file.write(f'__git_version__="{git_version}"\n') - - -def main() -> None: - parser = argparse.ArgumentParser() - parser.add_argument( - "-o", - "--outfile", - type=str, - help="Path to write version info to", - required=False, - ) - parser.add_argument( - "--print", - default=False, - action="store_true", - help="Whether to print out the version", - required=False, - ) - args = parser.parse_args() - - if args.outfile: - if not args.outfile.endswith(".py"): - raise ValueError( - f"Output file must be a Python file. " - f"Got: {args.outfile} as filename instead" - ) - - write_version_info(args.outfile) - - if args.print: - try: - import _version_meson - - version = _version_meson.__version__ - except ImportError: - version = versioneer.get_version() - print(version) - - -main() diff --git a/meson.build b/meson.build index 06623a305ab54..4281083f7c55b 100644 --- a/meson.build +++ b/meson.build @@ -2,7 +2,9 @@ project( 'pandas', 'c', 'cpp', 'cython', - version: run_command(['generate_version.py', '--print'], check: true).stdout().strip(), + version: run_command( + ['python', 'pandas/_build_utils/gitversion.py'], + check: true).stdout().strip(), license: 'BSD-3', meson_version: '>=1.2.1', default_options: [ @@ -15,8 +17,6 @@ project( fs = import('fs') py = import('python').find_installation(pure: false) tempita = files('generate_pxi.py') -versioneer = files('generate_version.py') - add_project_arguments('-DNPY_NO_DEPRECATED_API=0', language : 'c') add_project_arguments('-DNPY_NO_DEPRECATED_API=0', language : 'cpp') @@ -27,23 +27,6 @@ add_project_arguments('-DNPY_NO_DEPRECATED_API=0', language : 'cpp') add_project_arguments('-DNPY_TARGET_VERSION=NPY_1_21_API_VERSION', language : 'c') add_project_arguments('-DNPY_TARGET_VERSION=NPY_1_21_API_VERSION', language : 'cpp') - -if fs.exists('_version_meson.py') - py.install_sources('_version_meson.py', subdir: 'pandas') -else - custom_target('write_version_file', - output: '_version_meson.py', - command: [ - py, versioneer, '-o', '@OUTPUT@' - ], - build_by_default: true, - build_always_stale: true, - install: true, - install_dir: py.get_install_dir() / 'pandas' - ) - meson.add_dist_script(py, versioneer, '-o', '_version_meson.py') -endif - # Needed by pandas.test() when it looks for the pytest ini options py.install_sources( 'pyproject.toml', diff --git a/pandas/__init__.py b/pandas/__init__.py index f7ae91dd847f7..d055bd99ebfe8 100644 --- a/pandas/__init__.py +++ b/pandas/__init__.py @@ -171,22 +171,7 @@ from pandas.util._tester import test -# use the closest tagged version if possible -_built_with_meson = False -try: - from pandas._version_meson import ( # pyright: ignore [reportMissingImports] - __version__, - __git_version__, - ) - - _built_with_meson = True -except ImportError: - from pandas._version import get_versions - - v = get_versions() - __version__ = v.get("closest-tag", v["version"]) - __git_version__ = v.get("full-revisionid") - del get_versions, v +# If a version with git hash was stored, use that instead # module level doc-string diff --git a/pandas/_build_utils/gitversion.py b/pandas/_build_utils/gitversion.py new file mode 100755 index 0000000000000..363b10ce84f76 --- /dev/null +++ b/pandas/_build_utils/gitversion.py @@ -0,0 +1,90 @@ +# This file is vendored from NumPy. See NUMPY_LICENSE.txt + +#!/usr/bin/env python3 +import os +import textwrap + + +def init_version(): + init = os.path.join(os.path.dirname(__file__), "../../pyproject.toml") + with open(init) as fid: + data = fid.readlines() + + version_line = next(line for line in data if line.startswith("version =")) + + version = version_line.strip().split(" = ")[1] + version = version.replace('"', "").replace("'", "") + + return version + + +def git_version(version): + # Append last commit date and hash to dev version information, + # if available + + import os.path + import subprocess + + git_hash = "" + try: + p = subprocess.Popen( + ["git", "log", "-1", '--format="%H %aI"'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + cwd=os.path.dirname(__file__), + ) + except FileNotFoundError: + pass + else: + out, err = p.communicate() + if p.returncode == 0: + git_hash, git_date = ( + out.decode("utf-8") + .strip() + .replace('"', "") + .split("T")[0] + .replace("-", "") + .split() + ) + + # Only attach git tag to development versions + if "dev" in version: + version += f"+git{git_date}.{git_hash[:7]}" + + return version, git_hash + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser() + parser.add_argument("--write", help="Save version to this file") + parser.add_argument( + "--meson-dist", + help="Output path is relative to MESON_DIST_ROOT", + action="store_true", + ) + args = parser.parse_args() + + version, git_hash = git_version(init_version()) + + template = textwrap.dedent(f""" + version = "{version}" + __version__ = version + """) + + if args.write: + outfile = args.write + if args.meson_dist: + outfile = os.path.join(os.environ.get("MESON_DIST_ROOT", ""), outfile) + + # Print human readable output path + relpath = os.path.relpath(outfile) + if relpath.startswith("."): + relpath = outfile + + with open(outfile, "w") as f: + print(f"Saving version to {relpath}") + f.write(template) + else: + print(version) diff --git a/pandas/_version.py b/pandas/_version.py deleted file mode 100644 index 7bd9da2bb1cfa..0000000000000 --- a/pandas/_version.py +++ /dev/null @@ -1,692 +0,0 @@ -# This file helps to compute a version number in source trees obtained from -# git-archive tarball (such as those provided by githubs download-from-tag -# feature). Distribution tarballs (built by setup.py sdist) and build -# directories (produced by setup.py build) will contain a much shorter file -# that just contains the computed version number. - -# This file is released into the public domain. -# Generated by versioneer-0.28 -# https://github.com/python-versioneer/python-versioneer - -"""Git implementation of _version.py.""" - -import errno -import functools -import os -import re -import subprocess -import sys -from typing import Callable - - -def get_keywords(): - """Get the keywords needed to look up the version information.""" - # these strings will be replaced by git during git-archive. - # setup.py/versioneer.py will grep for the variable names, so they must - # each be defined on a line of their own. _version.py will just call - # get_keywords(). - git_refnames = "$Format:%d$" - git_full = "$Format:%H$" - git_date = "$Format:%ci$" - keywords = {"refnames": git_refnames, "full": git_full, "date": git_date} - return keywords - - -class VersioneerConfig: - """Container for Versioneer configuration parameters.""" - - -def get_config(): - """Create, populate and return the VersioneerConfig() object.""" - # these strings are filled in when 'setup.py versioneer' creates - # _version.py - cfg = VersioneerConfig() - cfg.VCS = "git" - cfg.style = "pep440" - cfg.tag_prefix = "v" - cfg.parentdir_prefix = "pandas-" - cfg.versionfile_source = "pandas/_version.py" - cfg.verbose = False - return cfg - - -class NotThisMethod(Exception): - """Exception raised if a method is not valid for the current scenario.""" - - -LONG_VERSION_PY: dict[str, str] = {} -HANDLERS: dict[str, dict[str, Callable]] = {} - - -def register_vcs_handler(vcs, method): # decorator - """Create decorator to mark a method as the handler of a VCS.""" - - def decorate(f): - """Store f in HANDLERS[vcs][method].""" - if vcs not in HANDLERS: - HANDLERS[vcs] = {} - HANDLERS[vcs][method] = f - return f - - return decorate - - -def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, env=None): - """Call the given command(s).""" - assert isinstance(commands, list) - process = None - - popen_kwargs = {} - if sys.platform == "win32": - # This hides the console window if pythonw.exe is used - startupinfo = subprocess.STARTUPINFO() - startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW - popen_kwargs["startupinfo"] = startupinfo - - for command in commands: - dispcmd = str([command] + args) - try: - # remember shell=False, so use git.cmd on windows, not just git - process = subprocess.Popen( - [command] + args, - cwd=cwd, - env=env, - stdout=subprocess.PIPE, - stderr=(subprocess.PIPE if hide_stderr else None), - **popen_kwargs, - ) - break - except OSError: - e = sys.exc_info()[1] - if e.errno == errno.ENOENT: - continue - if verbose: - print(f"unable to run {dispcmd}") - print(e) - return None, None - else: - if verbose: - print(f"unable to find command, tried {commands}") - return None, None - stdout = process.communicate()[0].strip().decode() - if process.returncode != 0: - if verbose: - print(f"unable to run {dispcmd} (error)") - print(f"stdout was {stdout}") - return None, process.returncode - return stdout, process.returncode - - -def versions_from_parentdir(parentdir_prefix, root, verbose): - """Try to determine the version from the parent directory name. - - Source tarballs conventionally unpack into a directory that includes both - the project name and a version string. We will also support searching up - two directory levels for an appropriately named parent directory - """ - rootdirs = [] - - for _ in range(3): - dirname = os.path.basename(root) - if dirname.startswith(parentdir_prefix): - return { - "version": dirname[len(parentdir_prefix) :], - "full-revisionid": None, - "dirty": False, - "error": None, - "date": None, - } - rootdirs.append(root) - root = os.path.dirname(root) # up a level - - if verbose: - print( - f"Tried directories {rootdirs!s} \ - but none started with prefix {parentdir_prefix}" - ) - raise NotThisMethod("rootdir doesn't start with parentdir_prefix") - - -@register_vcs_handler("git", "get_keywords") -def git_get_keywords(versionfile_abs): - """Extract version information from the given file.""" - # the code embedded in _version.py can just fetch the value of these - # keywords. When used from setup.py, we don't want to import _version.py, - # so we do it with a regexp instead. This function is not used from - # _version.py. - keywords = {} - try: - with open(versionfile_abs, encoding="utf-8") as fobj: - for line in fobj: - if line.strip().startswith("git_refnames ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["refnames"] = mo.group(1) - if line.strip().startswith("git_full ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["full"] = mo.group(1) - if line.strip().startswith("git_date ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["date"] = mo.group(1) - except OSError: - pass - return keywords - - -@register_vcs_handler("git", "keywords") -def git_versions_from_keywords(keywords, tag_prefix, verbose): - """Get version information from git keywords.""" - if "refnames" not in keywords: - raise NotThisMethod("Short version file found") - date = keywords.get("date") - if date is not None: - # Use only the last line. Previous lines may contain GPG signature - # information. - date = date.splitlines()[-1] - - # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant - # datestamp. However we prefer "%ci" (which expands to an "ISO-8601 - # -like" string, which we must then edit to make compliant), because - # it's been around since git-1.5.3, and it's too difficult to - # discover which version we're using, or to work around using an - # older one. - date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) - refnames = keywords["refnames"].strip() - if refnames.startswith("$Format"): - if verbose: - print("keywords are unexpanded, not using") - raise NotThisMethod("unexpanded keywords, not a git-archive tarball") - refs = {r.strip() for r in refnames.strip("()").split(",")} - # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of - # just "foo-1.0". If we see a "tag: " prefix, prefer those. - TAG = "tag: " - tags = {r[len(TAG) :] for r in refs if r.startswith(TAG)} - if not tags: - # Either we're using git < 1.8.3, or there really are no tags. We use - # a heuristic: assume all version tags have a digit. The old git %d - # expansion behaves like git log --decorate=short and strips out the - # refs/heads/ and refs/tags/ prefixes that would let us distinguish - # between branches and tags. By ignoring refnames without digits, we - # filter out many common branch names like "release" and - # "stabilization", as well as "HEAD" and "master". - tags = {r for r in refs if re.search(r"\d", r)} - if verbose: - print(f"discarding '{','.join(refs - tags)}', no digits") - if verbose: - print(f"likely tags: {','.join(sorted(tags))}") - for ref in sorted(tags): - # sorting will prefer e.g. "2.0" over "2.0rc1" - if ref.startswith(tag_prefix): - r = ref[len(tag_prefix) :] - # Filter out refs that exactly match prefix or that don't start - # with a number once the prefix is stripped (mostly a concern - # when prefix is '') - if not re.match(r"\d", r): - continue - if verbose: - print(f"picking {r}") - return { - "version": r, - "full-revisionid": keywords["full"].strip(), - "dirty": False, - "error": None, - "date": date, - } - # no suitable tags, so version is "0+unknown", but full hex is still there - if verbose: - print("no suitable tags, using unknown + full revision id") - return { - "version": "0+unknown", - "full-revisionid": keywords["full"].strip(), - "dirty": False, - "error": "no suitable tags", - "date": None, - } - - -@register_vcs_handler("git", "pieces_from_vcs") -def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command): - """Get version from 'git describe' in the root of the source tree. - - This only gets called if the git-archive 'subst' keywords were *not* - expanded, and _version.py hasn't already been rewritten with a short - version string, meaning we're inside a checked out source tree. - """ - GITS = ["git"] - if sys.platform == "win32": - GITS = ["git.cmd", "git.exe"] - - # GIT_DIR can interfere with correct operation of Versioneer. - # It may be intended to be passed to the Versioneer-versioned project, - # but that should not change where we get our version from. - env = os.environ.copy() - env.pop("GIT_DIR", None) - runner = functools.partial(runner, env=env) - - _, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root, hide_stderr=not verbose) - if rc != 0: - if verbose: - print(f"Directory {root} not under git control") - raise NotThisMethod("'git rev-parse --git-dir' returned error") - - # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] - # if there isn't one, this yields HEX[-dirty] (no NUM) - describe_out, rc = runner( - GITS, - [ - "describe", - "--tags", - "--dirty", - "--always", - "--long", - "--match", - f"{tag_prefix}[[:digit:]]*", - ], - cwd=root, - ) - # --long was added in git-1.5.5 - if describe_out is None: - raise NotThisMethod("'git describe' failed") - describe_out = describe_out.strip() - full_out, rc = runner(GITS, ["rev-parse", "HEAD"], cwd=root) - if full_out is None: - raise NotThisMethod("'git rev-parse' failed") - full_out = full_out.strip() - - pieces = {} - pieces["long"] = full_out - pieces["short"] = full_out[:7] # maybe improved later - pieces["error"] = None - - branch_name, rc = runner(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], cwd=root) - # --abbrev-ref was added in git-1.6.3 - if rc != 0 or branch_name is None: - raise NotThisMethod("'git rev-parse --abbrev-ref' returned error") - branch_name = branch_name.strip() - - if branch_name == "HEAD": - # If we aren't exactly on a branch, pick a branch which represents - # the current commit. If all else fails, we are on a branchless - # commit. - branches, rc = runner(GITS, ["branch", "--contains"], cwd=root) - # --contains was added in git-1.5.4 - if rc != 0 or branches is None: - raise NotThisMethod("'git branch --contains' returned error") - branches = branches.split("\n") - - # Remove the first line if we're running detached - if "(" in branches[0]: - branches.pop(0) - - # Strip off the leading "* " from the list of branches. - branches = [branch[2:] for branch in branches] - if "master" in branches: - branch_name = "master" - elif not branches: - branch_name = None - else: - # Pick the first branch that is returned. Good or bad. - branch_name = branches[0] - - pieces["branch"] = branch_name - - # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] - # TAG might have hyphens. - git_describe = describe_out - - # look for -dirty suffix - dirty = git_describe.endswith("-dirty") - pieces["dirty"] = dirty - if dirty: - git_describe = git_describe[: git_describe.rindex("-dirty")] - - # now we have TAG-NUM-gHEX or HEX - - if "-" in git_describe: - # TAG-NUM-gHEX - mo = re.search(r"^(.+)-(\d+)-g([0-9a-f]+)$", git_describe) - if not mo: - # unparsable. Maybe git-describe is misbehaving? - pieces["error"] = f"unable to parse git-describe output: '{describe_out}'" - return pieces - - # tag - full_tag = mo.group(1) - if not full_tag.startswith(tag_prefix): - if verbose: - fmt = "tag '%s' doesn't start with prefix '%s'" - print(fmt % (full_tag, tag_prefix)) - pieces["error"] = ( - f"tag '{full_tag}' doesn't start with prefix '{tag_prefix}'" - ) - return pieces - pieces["closest-tag"] = full_tag[len(tag_prefix) :] - - # distance: number of commits since tag - pieces["distance"] = int(mo.group(2)) - - # commit: short hex revision ID - pieces["short"] = mo.group(3) - - else: - # HEX: no tags - pieces["closest-tag"] = None - out, rc = runner(GITS, ["rev-list", "HEAD", "--left-right"], cwd=root) - pieces["distance"] = len(out.split()) # total number of commits - - # commit date: see ISO-8601 comment in git_versions_from_keywords() - date = runner(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[0].strip() - # Use only the last line. Previous lines may contain GPG signature - # information. - date = date.splitlines()[-1] - pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) - - return pieces - - -def plus_or_dot(pieces) -> str: - """Return a + if we don't already have one, else return a .""" - if "+" in pieces.get("closest-tag", ""): - return "." - return "+" - - -def render_pep440(pieces): - """Build up version string, with post-release "local version identifier". - - Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you - get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty - - Exceptions: - 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += plus_or_dot(pieces) - rendered += f"{pieces['distance']}.g{pieces['short']}" - if pieces["dirty"]: - rendered += ".dirty" - else: - # exception #1 - rendered = f"0+untagged.{pieces['distance']}.g{pieces['short']}" - if pieces["dirty"]: - rendered += ".dirty" - return rendered - - -def render_pep440_branch(pieces): - """TAG[[.dev0]+DISTANCE.gHEX[.dirty]] . - - The ".dev0" means not master branch. Note that .dev0 sorts backwards - (a feature branch will appear "older" than the master branch). - - Exceptions: - 1: no tags. 0[.dev0]+untagged.DISTANCE.gHEX[.dirty] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - if pieces["branch"] != "master": - rendered += ".dev0" - rendered += plus_or_dot(pieces) - rendered += f"{pieces['distance']}.g{pieces['short']}" - if pieces["dirty"]: - rendered += ".dirty" - else: - # exception #1 - rendered = "0" - if pieces["branch"] != "master": - rendered += ".dev0" - rendered += f"+untagged.{pieces['distance']}.g{pieces['short']}" - if pieces["dirty"]: - rendered += ".dirty" - return rendered - - -def pep440_split_post(ver): - """Split pep440 version string at the post-release segment. - - Returns the release segments before the post-release and the - post-release version number (or -1 if no post-release segment is present). - """ - vc = str.split(ver, ".post") - return vc[0], int(vc[1] or 0) if len(vc) == 2 else None - - -def render_pep440_pre(pieces): - """TAG[.postN.devDISTANCE] -- No -dirty. - - Exceptions: - 1: no tags. 0.post0.devDISTANCE - """ - if pieces["closest-tag"]: - if pieces["distance"]: - # update the post release segment - tag_version, post_version = pep440_split_post(pieces["closest-tag"]) - rendered = tag_version - if post_version is not None: - rendered += f".post{post_version + 1}.dev{pieces['distance']}" - else: - rendered += f".post0.dev{pieces['distance']}" - else: - # no commits, use the tag as the version - rendered = pieces["closest-tag"] - else: - # exception #1 - rendered = f"0.post0.dev{pieces['distance']}" - return rendered - - -def render_pep440_post(pieces): - """TAG[.postDISTANCE[.dev0]+gHEX] . - - The ".dev0" means dirty. Note that .dev0 sorts backwards - (a dirty tree will appear "older" than the corresponding clean one), - but you shouldn't be releasing software with -dirty anyways. - - Exceptions: - 1: no tags. 0.postDISTANCE[.dev0] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += f".post{pieces['distance']}" - if pieces["dirty"]: - rendered += ".dev0" - rendered += plus_or_dot(pieces) - rendered += f"g{pieces['short']}" - else: - # exception #1 - rendered = f"0.post{pieces['distance']}" - if pieces["dirty"]: - rendered += ".dev0" - rendered += f"+g{pieces['short']}" - return rendered - - -def render_pep440_post_branch(pieces): - """TAG[.postDISTANCE[.dev0]+gHEX[.dirty]] . - - The ".dev0" means not master branch. - - Exceptions: - 1: no tags. 0.postDISTANCE[.dev0]+gHEX[.dirty] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += f".post{pieces['distance']}" - if pieces["branch"] != "master": - rendered += ".dev0" - rendered += plus_or_dot(pieces) - rendered += f"g{pieces['short']}" - if pieces["dirty"]: - rendered += ".dirty" - else: - # exception #1 - rendered = f"0.post{pieces['distance']}" - if pieces["branch"] != "master": - rendered += ".dev0" - rendered += f"+g{pieces['short']}" - if pieces["dirty"]: - rendered += ".dirty" - return rendered - - -def render_pep440_old(pieces): - """TAG[.postDISTANCE[.dev0]] . - - The ".dev0" means dirty. - - Exceptions: - 1: no tags. 0.postDISTANCE[.dev0] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += f"0.post{pieces['distance']}" - if pieces["dirty"]: - rendered += ".dev0" - else: - # exception #1 - rendered = f"0.post{pieces['distance']}" - if pieces["dirty"]: - rendered += ".dev0" - return rendered - - -def render_git_describe(pieces): - """TAG[-DISTANCE-gHEX][-dirty]. - - Like 'git describe --tags --dirty --always'. - - Exceptions: - 1: no tags. HEX[-dirty] (note: no 'g' prefix) - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"]: - rendered += f"-{pieces['distance']}-g{pieces['short']}" - else: - # exception #1 - rendered = pieces["short"] - if pieces["dirty"]: - rendered += "-dirty" - return rendered - - -def render_git_describe_long(pieces): - """TAG-DISTANCE-gHEX[-dirty]. - - Like 'git describe --tags --dirty --always -long'. - The distance/hash is unconditional. - - Exceptions: - 1: no tags. HEX[-dirty] (note: no 'g' prefix) - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - rendered += f"-{pieces['distance']}-g{pieces['short']}" - else: - # exception #1 - rendered = pieces["short"] - if pieces["dirty"]: - rendered += "-dirty" - return rendered - - -def render(pieces, style): - """Render the given version pieces into the requested style.""" - if pieces["error"]: - return { - "version": "unknown", - "full-revisionid": pieces.get("long"), - "dirty": None, - "error": pieces["error"], - "date": None, - } - - if not style or style == "default": - style = "pep440" # the default - - if style == "pep440": - rendered = render_pep440(pieces) - elif style == "pep440-branch": - rendered = render_pep440_branch(pieces) - elif style == "pep440-pre": - rendered = render_pep440_pre(pieces) - elif style == "pep440-post": - rendered = render_pep440_post(pieces) - elif style == "pep440-post-branch": - rendered = render_pep440_post_branch(pieces) - elif style == "pep440-old": - rendered = render_pep440_old(pieces) - elif style == "git-describe": - rendered = render_git_describe(pieces) - elif style == "git-describe-long": - rendered = render_git_describe_long(pieces) - else: - raise ValueError(f"unknown style '{style}'") - - return { - "version": rendered, - "full-revisionid": pieces["long"], - "dirty": pieces["dirty"], - "error": None, - "date": pieces.get("date"), - } - - -def get_versions(): - """Get version information or return default if unable to do so.""" - # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have - # __file__, we can work backwards from there to the root. Some - # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which - # case we can only use expanded keywords. - - cfg = get_config() - verbose = cfg.verbose - - try: - return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, verbose) - except NotThisMethod: - pass - - try: - root = os.path.realpath(__file__) - # versionfile_source is the relative path from the top of the source - # tree (where the .git directory might live) to this file. Invert - # this to find the root from __file__. - for _ in cfg.versionfile_source.split("/"): - root = os.path.dirname(root) - except NameError: - return { - "version": "0+unknown", - "full-revisionid": None, - "dirty": None, - "error": "unable to find root of source tree", - "date": None, - } - - try: - pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) - return render(pieces, cfg.style) - except NotThisMethod: - pass - - try: - if cfg.parentdir_prefix: - return versions_from_parentdir(cfg.parentdir_prefix, root, verbose) - except NotThisMethod: - pass - - return { - "version": "0+unknown", - "full-revisionid": None, - "dirty": None, - "error": "unable to compute version", - "date": None, - } diff --git a/pandas/meson.build b/pandas/meson.build index 435103a954d86..9e5f250f5d3d8 100644 --- a/pandas/meson.build +++ b/pandas/meson.build @@ -22,6 +22,33 @@ inc_pd = include_directories('_libs/include') fs.copyfile('__init__.py') +pd_dir = py.get_install_dir() / 'pandas' + +# Generate version.py for sdist +meson.add_dist_script( + ['_build_utils/gitversion.py', '--meson-dist', '--write', + 'pandas/version.py'] +) +if not fs.exists('version.py') + generate_version = custom_target( + 'generate-version', + install: true, + build_always_stale: true, + build_by_default: true, + output: 'version.py', + input: '_build_utils/gitversion.py', + command: [py, '@INPUT@', '--write', '@OUTPUT@'], + install_dir: pd_dir, + install_tag: 'python-runtime' + ) +else + # When building from sdist, version.py exists and should be included + py.install_sources( + ['version.py'], + subdir : 'pandas' + ) +endif + subdir('_libs') subdirs_list = [ @@ -45,7 +72,6 @@ endforeach top_level_py_list = [ '__init__.py', '_typing.py', - '_version.py', 'conftest.py', 'testing.py' ] diff --git a/pyproject.toml b/pyproject.toml index f96fbee4a5818..ad4752b2692eb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,9 +19,7 @@ build-backend = "mesonpy" [project] name = 'pandas' -dynamic = [ - 'version' -] +version = "3.0.0.dev0" description = 'Powerful data structures for data analysis, time series, and statistics' readme = 'README.md' authors = [ From 2df3ae3d5a8b07191cfbc785b045b774a65da0e2 Mon Sep 17 00:00:00 2001 From: Will Ayd Date: Wed, 20 Mar 2024 18:51:43 -0400 Subject: [PATCH 02/17] ignore ruff --- pandas/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pandas/__init__.py b/pandas/__init__.py index d055bd99ebfe8..d703b30ecce11 100644 --- a/pandas/__init__.py +++ b/pandas/__init__.py @@ -172,6 +172,8 @@ from pandas.util._tester import test # If a version with git hash was stored, use that instead +from pandas import version +from pandas.version import __version__ # module level doc-string From 265b2998886eb1d02cb7c425e63c86aad979f57d Mon Sep 17 00:00:00 2001 From: Will Ayd Date: Wed, 20 Mar 2024 20:02:03 -0400 Subject: [PATCH 03/17] ignore import --- pandas/__init__.py | 9 +++++++-- pandas/_build_utils/gitversion.py | 1 + pandas/meson.build | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/pandas/__init__.py b/pandas/__init__.py index d703b30ecce11..88ba006b9780c 100644 --- a/pandas/__init__.py +++ b/pandas/__init__.py @@ -172,8 +172,11 @@ from pandas.util._tester import test # If a version with git hash was stored, use that instead -from pandas import version -from pandas.version import __version__ +_built_with_meson = True +from . import _version + +__version__ = _version.__version__ +__git_version__ = _version.git_revision # module level doc-string @@ -221,6 +224,8 @@ # Pandas is not (yet) a py.typed library: the public API is determined # based on the documentation. __all__ = [ + "__version__", + "__git_version__", "ArrowDtype", "BooleanDtype", "Categorical", diff --git a/pandas/_build_utils/gitversion.py b/pandas/_build_utils/gitversion.py index 363b10ce84f76..c9ca3b78d01df 100755 --- a/pandas/_build_utils/gitversion.py +++ b/pandas/_build_utils/gitversion.py @@ -71,6 +71,7 @@ def git_version(version): template = textwrap.dedent(f""" version = "{version}" __version__ = version + git_revision = "{git_hash}" """) if args.write: diff --git a/pandas/meson.build b/pandas/meson.build index 9e5f250f5d3d8..10348c019ed14 100644 --- a/pandas/meson.build +++ b/pandas/meson.build @@ -44,7 +44,7 @@ if not fs.exists('version.py') else # When building from sdist, version.py exists and should be included py.install_sources( - ['version.py'], + ['_version.py'], subdir : 'pandas' ) endif From 20ce128c48e4cfcce434c6ff7c6d89f5d0a7bb49 Mon Sep 17 00:00:00 2001 From: Will Ayd Date: Wed, 20 Mar 2024 20:31:55 -0400 Subject: [PATCH 04/17] fix typo --- pandas/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/meson.build b/pandas/meson.build index 10348c019ed14..2ec17448ad13a 100644 --- a/pandas/meson.build +++ b/pandas/meson.build @@ -35,7 +35,7 @@ if not fs.exists('version.py') install: true, build_always_stale: true, build_by_default: true, - output: 'version.py', + output: '_version.py', input: '_build_utils/gitversion.py', command: [py, '@INPUT@', '--write', '@OUTPUT@'], install_dir: pd_dir, From 95c8df73557512fdcfabdb46a6b1a1d99dd51b9b Mon Sep 17 00:00:00 2001 From: Will Ayd Date: Wed, 20 Mar 2024 21:17:28 -0400 Subject: [PATCH 05/17] Fix failing tests --- pandas/__init__.py | 2 -- pandas/tests/api/test_api.py | 2 +- pandas/util/_print_versions.py | 12 ++---------- scripts/validate_unwanted_patterns.py | 2 +- 4 files changed, 4 insertions(+), 14 deletions(-) diff --git a/pandas/__init__.py b/pandas/__init__.py index 88ba006b9780c..7dd02bb9fea5b 100644 --- a/pandas/__init__.py +++ b/pandas/__init__.py @@ -224,8 +224,6 @@ # Pandas is not (yet) a py.typed library: the public API is determined # based on the documentation. __all__ = [ - "__version__", - "__git_version__", "ArrowDtype", "BooleanDtype", "Categorical", diff --git a/pandas/tests/api/test_api.py b/pandas/tests/api/test_api.py index 15b6c9abaea8f..ce028a2fc4675 100644 --- a/pandas/tests/api/test_api.py +++ b/pandas/tests/api/test_api.py @@ -33,7 +33,7 @@ def check(self, namespace, expected, ignored=None): class TestPDApi(Base): # these are optionally imported based on testing # & need to be ignored - ignored = ["tests", "locale", "conftest", "_version_meson"] + ignored = ["tests", "locale", "conftest", "_version"] # top-level sub-packages public_lib = [ diff --git a/pandas/util/_print_versions.py b/pandas/util/_print_versions.py index 6cdd96996cea6..9a2752fd20e49 100644 --- a/pandas/util/_print_versions.py +++ b/pandas/util/_print_versions.py @@ -24,17 +24,9 @@ def _get_commit_hash() -> str | None: Use vendored versioneer code to get git hash, which handles git worktree correctly. """ - try: - from pandas._version_meson import ( # pyright: ignore [reportMissingImports] - __git_version__, - ) + from pandas._version import git_revision # pyright: ignore [reportMissingImports] - return __git_version__ - except ImportError: - from pandas._version import get_versions - - versions = get_versions() # type: ignore[no-untyped-call] - return versions["full-revisionid"] + return git_revision def _get_sys_info() -> dict[str, JSONSerializable]: diff --git a/scripts/validate_unwanted_patterns.py b/scripts/validate_unwanted_patterns.py index a732d3f83a40a..719af4f40e7f9 100755 --- a/scripts/validate_unwanted_patterns.py +++ b/scripts/validate_unwanted_patterns.py @@ -48,7 +48,7 @@ "_global_config", "_chained_assignment_msg", "_chained_assignment_method_msg", - "_version_meson", + "_version", # The numba extensions need this to mock the iloc object "_iLocIndexer", # TODO(4.0): GH#55043 - remove upon removal of CoW option From 31aeba4003a62071c362497c999901b5ee4a96a4 Mon Sep 17 00:00:00 2001 From: Will Ayd Date: Wed, 20 Mar 2024 21:49:17 -0400 Subject: [PATCH 06/17] typing --- pandas/_build_utils/gitversion.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/_build_utils/gitversion.py b/pandas/_build_utils/gitversion.py index c9ca3b78d01df..1bfe27c00f010 100755 --- a/pandas/_build_utils/gitversion.py +++ b/pandas/_build_utils/gitversion.py @@ -5,7 +5,7 @@ import textwrap -def init_version(): +def init_version() -> str: init = os.path.join(os.path.dirname(__file__), "../../pyproject.toml") with open(init) as fid: data = fid.readlines() @@ -18,7 +18,7 @@ def init_version(): return version -def git_version(version): +def git_version(version) -> tuple[str, str]: # Append last commit date and hash to dev version information, # if available From ef1ac30b665d8625ae040b5fa81f21dc857a9f0a Mon Sep 17 00:00:00 2001 From: Will Ayd Date: Wed, 20 Mar 2024 22:00:31 -0400 Subject: [PATCH 07/17] fix shebang --- pandas/_build_utils/gitversion.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pandas/_build_utils/gitversion.py b/pandas/_build_utils/gitversion.py index 1bfe27c00f010..8b20723ba6222 100755 --- a/pandas/_build_utils/gitversion.py +++ b/pandas/_build_utils/gitversion.py @@ -1,6 +1,7 @@ +#!/usr/bin/env python3 + # This file is vendored from NumPy. See NUMPY_LICENSE.txt -#!/usr/bin/env python3 import os import textwrap From 3a982ff8540cf5c28264d4a552b5cb89beb406e9 Mon Sep 17 00:00:00 2001 From: Will Ayd Date: Wed, 20 Mar 2024 22:12:58 -0400 Subject: [PATCH 08/17] fix typo --- pandas/meson.build | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/meson.build b/pandas/meson.build index 2ec17448ad13a..0f7b5e5e00910 100644 --- a/pandas/meson.build +++ b/pandas/meson.build @@ -27,9 +27,9 @@ pd_dir = py.get_install_dir() / 'pandas' # Generate version.py for sdist meson.add_dist_script( ['_build_utils/gitversion.py', '--meson-dist', '--write', - 'pandas/version.py'] + 'pandas/_version.py'] ) -if not fs.exists('version.py') +if not fs.exists('_version.py') generate_version = custom_target( 'generate-version', install: true, From 13f1a9e5c094a2f19c3e7d7b4c981cd455e6987c Mon Sep 17 00:00:00 2001 From: Will Ayd Date: Wed, 20 Mar 2024 22:44:46 -0400 Subject: [PATCH 09/17] fix more typing --- pandas/_build_utils/gitversion.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/_build_utils/gitversion.py b/pandas/_build_utils/gitversion.py index 8b20723ba6222..6ff653f7db4e3 100755 --- a/pandas/_build_utils/gitversion.py +++ b/pandas/_build_utils/gitversion.py @@ -19,7 +19,7 @@ def init_version() -> str: return version -def git_version(version) -> tuple[str, str]: +def git_version(version: str) -> tuple[str, str]: # Append last commit date and hash to dev version information, # if available From 525415deacffdf4fc3ace44800a5adcf5cf28e41 Mon Sep 17 00:00:00 2001 From: Will Ayd Date: Wed, 20 Mar 2024 22:47:57 -0400 Subject: [PATCH 10/17] remove versioneer / setuptools --- .circleci/config.yml | 2 +- .github/workflows/unit-tests.yml | 5 +- ci/deps/actions-310.yaml | 1 - ci/deps/actions-311-downstream_compat.yaml | 1 - ci/deps/actions-311-numpydev.yaml | 1 - ci/deps/actions-311-pyarrownightly.yaml | 1 - ci/deps/actions-311.yaml | 1 - ci/deps/actions-312.yaml | 1 - ci/deps/actions-39-minimum_versions.yaml | 1 - ci/deps/actions-39.yaml | 1 - ci/deps/actions-pypy-39.yaml | 1 - ci/deps/circle-310-arm64.yaml | 1 - ci/meta.yaml | 1 - environment.yml | 1 - gitpod/gitpod.Dockerfile | 2 +- pandas/util/_print_versions.py | 2 +- pyproject.toml | 12 - requirements-dev.txt | 1 - scripts/tests/data/deps_expected_random.yaml | 1 - scripts/tests/data/deps_minimum.toml | 13 - .../tests/data/deps_unmodified_random.yaml | 1 - setup.py | 686 ------------------ 22 files changed, 5 insertions(+), 732 deletions(-) delete mode 100755 setup.py diff --git a/.circleci/config.yml b/.circleci/config.yml index ea93575ac9430..8d9008d05aa1b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -34,7 +34,7 @@ jobs: /opt/python/cp311-cp311/bin/python -m venv ~/virtualenvs/pandas-dev . ~/virtualenvs/pandas-dev/bin/activate python -m pip install --no-cache-dir -U pip wheel setuptools meson-python==0.13.1 meson[ninja]==1.2.1 - python -m pip install --no-cache-dir versioneer[toml] cython numpy python-dateutil pytz pytest>=7.3.2 pytest-xdist>=2.2.0 hypothesis>=6.46.1 + python -m pip install --no-cache-dir cython numpy python-dateutil pytz pytest>=7.3.2 pytest-xdist>=2.2.0 hypothesis>=6.46.1 python -m pip install --no-cache-dir --no-build-isolation -e . --config-settings=setup-args="--werror" python -m pip list --no-cache-dir - run: | diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index f93950224eaae..5a85da1a25eb3 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -228,7 +228,7 @@ jobs: . ~/virtualenvs/pandas-dev/bin/activate python -m pip install --no-cache-dir -U pip wheel setuptools meson[ninja]==1.2.1 meson-python==0.13.1 python -m pip install numpy --config-settings=setup-args="-Dallow-noblas=true" - python -m pip install --no-cache-dir versioneer[toml] cython python-dateutil pytz pytest>=7.3.2 pytest-xdist>=2.2.0 hypothesis>=6.46.1 + python -m pip install --no-cache-dir cython python-dateutil pytz pytest>=7.3.2 pytest-xdist>=2.2.0 hypothesis>=6.46.1 python -m pip install --no-cache-dir --no-build-isolation -e . --config-settings=setup-args="--werror" python -m pip list --no-cache-dir export PANDAS_CI=1 @@ -266,7 +266,7 @@ jobs: /opt/python/cp311-cp311/bin/python -m venv ~/virtualenvs/pandas-dev . ~/virtualenvs/pandas-dev/bin/activate python -m pip install --no-cache-dir -U pip wheel setuptools meson-python==0.13.1 meson[ninja]==1.2.1 - python -m pip install --no-cache-dir versioneer[toml] cython numpy python-dateutil pytz pytest>=7.3.2 pytest-xdist>=2.2.0 hypothesis>=6.46.1 + python -m pip install --no-cache-dir cython numpy python-dateutil pytz pytest>=7.3.2 pytest-xdist>=2.2.0 hypothesis>=6.46.1 python -m pip install --no-cache-dir --no-build-isolation -e . --config-settings=setup-args="--werror" python -m pip list --no-cache-dir @@ -339,7 +339,6 @@ jobs: python --version python -m pip install --upgrade pip setuptools wheel meson[ninja]==1.2.1 meson-python==0.13.1 python -m pip install --pre --extra-index-url https://pypi.anaconda.org/scientific-python-nightly-wheels/simple numpy - python -m pip install versioneer[toml] python -m pip install python-dateutil pytz tzdata cython hypothesis>=6.46.1 pytest>=7.3.2 pytest-xdist>=2.2.0 pytest-cov python -m pip install -ve . --no-build-isolation --no-index --no-deps --config-settings=setup-args="--werror" python -m pip list diff --git a/ci/deps/actions-310.yaml b/ci/deps/actions-310.yaml index 85ee5230b31be..779bb32e115a7 100644 --- a/ci/deps/actions-310.yaml +++ b/ci/deps/actions-310.yaml @@ -5,7 +5,6 @@ dependencies: - python=3.10 # build dependencies - - versioneer[toml] - cython>=0.29.33 - meson[ninja]=1.2.1 - meson-python=0.13.1 diff --git a/ci/deps/actions-311-downstream_compat.yaml b/ci/deps/actions-311-downstream_compat.yaml index efd790d77afbb..2b5f9b08d1117 100644 --- a/ci/deps/actions-311-downstream_compat.yaml +++ b/ci/deps/actions-311-downstream_compat.yaml @@ -6,7 +6,6 @@ dependencies: - python=3.11 # build dependencies - - versioneer[toml] - cython>=0.29.33 - meson[ninja]=1.2.1 - meson-python=0.13.1 diff --git a/ci/deps/actions-311-numpydev.yaml b/ci/deps/actions-311-numpydev.yaml index b62e8630f2059..b109e45110b63 100644 --- a/ci/deps/actions-311-numpydev.yaml +++ b/ci/deps/actions-311-numpydev.yaml @@ -5,7 +5,6 @@ dependencies: - python=3.11 # build dependencies - - versioneer[toml] - meson[ninja]=1.2.1 - meson-python=0.13.1 - cython>=0.29.33 diff --git a/ci/deps/actions-311-pyarrownightly.yaml b/ci/deps/actions-311-pyarrownightly.yaml index d84063ac2a9ba..12b021ad20c07 100644 --- a/ci/deps/actions-311-pyarrownightly.yaml +++ b/ci/deps/actions-311-pyarrownightly.yaml @@ -5,7 +5,6 @@ dependencies: - python=3.11 # build dependencies - - versioneer[toml] - meson[ninja]=1.2.1 - cython>=0.29.33 - meson-python=0.13.1 diff --git a/ci/deps/actions-311.yaml b/ci/deps/actions-311.yaml index 535c260582eec..1177fe5df4f42 100644 --- a/ci/deps/actions-311.yaml +++ b/ci/deps/actions-311.yaml @@ -5,7 +5,6 @@ dependencies: - python=3.11 # build dependencies - - versioneer[toml] - cython>=0.29.33 - meson[ninja]=1.2.1 - meson-python=0.13.1 diff --git a/ci/deps/actions-312.yaml b/ci/deps/actions-312.yaml index 8b3f19f55e4b6..ef9b11542b781 100644 --- a/ci/deps/actions-312.yaml +++ b/ci/deps/actions-312.yaml @@ -5,7 +5,6 @@ dependencies: - python=3.12 # build dependencies - - versioneer[toml] - cython>=0.29.33 - meson[ninja]=1.2.1 - meson-python=0.13.1 diff --git a/ci/deps/actions-39-minimum_versions.yaml b/ci/deps/actions-39-minimum_versions.yaml index 94cb21d1621b6..67121ffdb270c 100644 --- a/ci/deps/actions-39-minimum_versions.yaml +++ b/ci/deps/actions-39-minimum_versions.yaml @@ -7,7 +7,6 @@ dependencies: - python=3.9 # build dependencies - - versioneer[toml] - cython>=0.29.33 - meson[ninja]=1.2.1 - meson-python=0.13.1 diff --git a/ci/deps/actions-39.yaml b/ci/deps/actions-39.yaml index 4cc9b1fbe2491..643881a326662 100644 --- a/ci/deps/actions-39.yaml +++ b/ci/deps/actions-39.yaml @@ -5,7 +5,6 @@ dependencies: - python=3.9 # build dependencies - - versioneer[toml] - cython>=0.29.33 - meson[ninja]=1.2.1 - meson-python=0.13.1 diff --git a/ci/deps/actions-pypy-39.yaml b/ci/deps/actions-pypy-39.yaml index d9c8dd81b7c33..279fc1dd1d4a2 100644 --- a/ci/deps/actions-pypy-39.yaml +++ b/ci/deps/actions-pypy-39.yaml @@ -8,7 +8,6 @@ dependencies: - python=3.9[build=*_pypy] # build dependencies - - versioneer[toml] - cython>=0.29.33 - meson[ninja]=1.2.1 - meson-python=0.13.1 diff --git a/ci/deps/circle-310-arm64.yaml b/ci/deps/circle-310-arm64.yaml index 869aae8596681..7962222802842 100644 --- a/ci/deps/circle-310-arm64.yaml +++ b/ci/deps/circle-310-arm64.yaml @@ -5,7 +5,6 @@ dependencies: - python=3.10 # build dependencies - - versioneer[toml] - cython>=0.29.33 - meson[ninja]=1.2.1 - meson-python=0.13.1 diff --git a/ci/meta.yaml b/ci/meta.yaml index aac5593e493b7..8eae47531dac9 100644 --- a/ci/meta.yaml +++ b/ci/meta.yaml @@ -30,7 +30,6 @@ requirements: - cython >=0.29.33,<3 - numpy >=1.21.6 # [py<311] - numpy >=1.23.2 # [py>=311] - - versioneer - tomli # [py<311] run: - python diff --git a/environment.yml b/environment.yml index e7bf2556d27f8..029c73f1c89a6 100644 --- a/environment.yml +++ b/environment.yml @@ -7,7 +7,6 @@ dependencies: - pip # build dependencies - - versioneer[toml] - cython~=3.0.5 - meson[ninja]=1.2.1 - meson-python=0.13.1 diff --git a/gitpod/gitpod.Dockerfile b/gitpod/gitpod.Dockerfile index ab3f25b231e67..884972d58eb0f 100644 --- a/gitpod/gitpod.Dockerfile +++ b/gitpod/gitpod.Dockerfile @@ -4,7 +4,7 @@ ARG BASE_CONTAINER="pandas/pandas-dev:latest" FROM gitpod/workspace-base:latest as clone -# the clone should be deep enough for versioneer to work +# the clone should be deep enough for version detection to work RUN git clone https://github.com/pandas-dev/pandas --depth 12 /tmp/pandas # ----------------------------------------------------------------------------- diff --git a/pandas/util/_print_versions.py b/pandas/util/_print_versions.py index 9a2752fd20e49..6a199ba7ba146 100644 --- a/pandas/util/_print_versions.py +++ b/pandas/util/_print_versions.py @@ -21,7 +21,7 @@ def _get_commit_hash() -> str | None: """ - Use vendored versioneer code to get git hash, which handles + Use vendored versioning code to get git hash, which handles git worktree correctly. """ from pandas._version import git_revision # pyright: ignore [reportMissingImports] diff --git a/pyproject.toml b/pyproject.toml index ad4752b2692eb..b3c4b5d21f49a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,6 @@ requires = [ # Pin <2.0 for releases until tested against an RC. But explicitly allow # testing the `.dev0` nightlies (which require the extra index). "numpy>1.22.4,<=2.0.0.dev0", - "versioneer[toml]" ] build-backend = "mesonpy" @@ -132,17 +131,6 @@ namespaces = false [tool.setuptools.exclude-package-data] "*" = ["*.c", "*.h"] -# See the docstring in versioneer.py for instructions. Note that you must -# re-run 'versioneer.py setup' after changing this section, and commit the -# resulting files. -[tool.versioneer] -VCS = "git" -style = "pep440" -versionfile_source = "pandas/_version.py" -versionfile_build = "pandas/_version.py" -tag_prefix = "v" -parentdir_prefix = "pandas-" - [tool.meson-python.args] setup = ['--vsenv'] # For Windows diff --git a/requirements-dev.txt b/requirements-dev.txt index 0cc064d2660bb..8aff3deda1c6e 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -2,7 +2,6 @@ # See that file for comments about the need/usage of each dependency. pip -versioneer[toml] cython~=3.0.5 meson[ninja]==1.2.1 meson-python==0.13.1 diff --git a/scripts/tests/data/deps_expected_random.yaml b/scripts/tests/data/deps_expected_random.yaml index 7bb95d05afb45..1566132ae3279 100644 --- a/scripts/tests/data/deps_expected_random.yaml +++ b/scripts/tests/data/deps_expected_random.yaml @@ -6,7 +6,6 @@ dependencies: - python=3.8 # build dependencies - - versioneer[toml] - cython>=0.29.32 # test dependencies diff --git a/scripts/tests/data/deps_minimum.toml b/scripts/tests/data/deps_minimum.toml index ca1dc0c961c42..8c98af0f8113c 100644 --- a/scripts/tests/data/deps_minimum.toml +++ b/scripts/tests/data/deps_minimum.toml @@ -6,7 +6,6 @@ requires = [ "wheel", "Cython>=0.29.32,<3", # Note: sync with setup.py, environment.yml and asv.conf.json "oldest-supported-numpy>=2022.8.16", - "versioneer[toml]" ] # build-backend = "setuptools.build_meta" @@ -126,17 +125,6 @@ namespaces = false [tool.setuptools.exclude-package-data] "*" = ["*.c", "*.h"] -# See the docstring in versioneer.py for instructions. Note that you must -# re-run 'versioneer.py setup' after changing this section, and commit the -# resulting files. -[tool.versioneer] -VCS = "git" -style = "pep440" -versionfile_source = "pandas/_version.py" -versionfile_build = "pandas/_version.py" -tag_prefix = "v" -parentdir_prefix = "pandas-" - [tool.cibuildwheel] skip = "cp36-* cp37-* pp37-* *-manylinux_i686 *_ppc64le *_s390x *-musllinux*" build-verbosity = "3" @@ -226,7 +214,6 @@ exclude = [ "doc/build/*.py", "doc/temp/*.py", ".eggs/*.py", - "versioneer.py", # exclude asv benchmark environments from linting "env", ] diff --git a/scripts/tests/data/deps_unmodified_random.yaml b/scripts/tests/data/deps_unmodified_random.yaml index 49299aa078ce4..5225c7a1984eb 100644 --- a/scripts/tests/data/deps_unmodified_random.yaml +++ b/scripts/tests/data/deps_unmodified_random.yaml @@ -6,7 +6,6 @@ dependencies: - python=3.8 # build dependencies - - versioneer[toml] - cython>=0.29.32 # test dependencies diff --git a/setup.py b/setup.py deleted file mode 100755 index 737ebd270d1e4..0000000000000 --- a/setup.py +++ /dev/null @@ -1,686 +0,0 @@ -#!/usr/bin/env python3 - -""" -Parts of this file were taken from the pyzmq project -(https://github.com/zeromq/pyzmq) which have been permitted for use under the -BSD license. Parts are from lxml (https://github.com/lxml/lxml) -""" - -import argparse -import multiprocessing -import os -from os.path import join as pjoin -import platform -import shutil -import sys -from sysconfig import get_config_vars - -import numpy -from pkg_resources import parse_version -from setuptools import ( - Command, - Extension, - setup, -) -from setuptools.command.build_ext import build_ext as _build_ext -import versioneer - -cmdclass = versioneer.get_cmdclass() - - -def is_platform_windows(): - return sys.platform in ("win32", "cygwin") - - -def is_platform_mac(): - return sys.platform == "darwin" - - -# note: sync with pyproject.toml, environment.yml and asv.conf.json -min_cython_ver = "3.0" - -try: - from Cython import ( - Tempita, - __version__ as _CYTHON_VERSION, - ) - from Cython.Build import cythonize - - _CYTHON_INSTALLED = parse_version(_CYTHON_VERSION) >= parse_version(min_cython_ver) -except ImportError: - _CYTHON_VERSION = None - _CYTHON_INSTALLED = False - cythonize = lambda x, *args, **kwargs: x # dummy func - - -_pxi_dep_template = { - "algos": ["_libs/algos_common_helper.pxi.in", "_libs/algos_take_helper.pxi.in"], - "hashtable": [ - "_libs/hashtable_class_helper.pxi.in", - "_libs/hashtable_func_helper.pxi.in", - "_libs/khash_for_primitive_helper.pxi.in", - ], - "index": ["_libs/index_class_helper.pxi.in"], - "sparse": ["_libs/sparse_op_helper.pxi.in"], - "interval": ["_libs/intervaltree.pxi.in"], -} - -_pxifiles = [] -_pxi_dep = {} -for module, files in _pxi_dep_template.items(): - pxi_files = [pjoin("pandas", x) for x in files] - _pxifiles.extend(pxi_files) - _pxi_dep[module] = pxi_files - - -class build_ext(_build_ext): - @classmethod - def render_templates(cls, pxifiles) -> None: - for pxifile in pxifiles: - # build pxifiles first, template extension must be .pxi.in - assert pxifile.endswith(".pxi.in") - outfile = pxifile[:-3] - - if ( - os.path.exists(outfile) - and os.stat(pxifile).st_mtime < os.stat(outfile).st_mtime - ): - # if .pxi.in is not updated, no need to output .pxi - continue - - with open(pxifile, encoding="utf-8") as f: - tmpl = f.read() - pyxcontent = Tempita.sub(tmpl) - - with open(outfile, "w", encoding="utf-8") as f: - f.write(pyxcontent) - - def build_extensions(self) -> None: - # if building from c files, don't need to - # generate template output - if _CYTHON_INSTALLED: - self.render_templates(_pxifiles) - - super().build_extensions() - - -class CleanCommand(Command): - """Custom command to clean the .so and .pyc files.""" - - user_options = [("all", "a", "")] - - def initialize_options(self) -> None: - self.all = True - self._clean_me = [] - self._clean_trees = [] - - base = pjoin("pandas", "_libs", "src") - parser = pjoin(base, "parser") - vendored = pjoin(base, "vendored") - dt = pjoin(base, "datetime") - ujson_python = pjoin(vendored, "ujson", "python") - ujson_lib = pjoin(vendored, "ujson", "lib") - self._clean_exclude = [ - pjoin(vendored, "numpy", "datetime", "np_datetime.c"), - pjoin(vendored, "numpy", "datetime", "np_datetime_strings.c"), - pjoin(dt, "date_conversions.c"), - pjoin(parser, "tokenizer.c"), - pjoin(parser, "io.c"), - pjoin(ujson_python, "ujson.c"), - pjoin(ujson_python, "objToJSON.c"), - pjoin(ujson_python, "JSONtoObj.c"), - pjoin(ujson_lib, "ultrajsonenc.c"), - pjoin(ujson_lib, "ultrajsondec.c"), - pjoin(dt, "pd_datetime.c"), - pjoin(parser, "pd_parser.c"), - ] - - for root, dirs, files in os.walk("pandas"): - for f in files: - filepath = pjoin(root, f) - if filepath in self._clean_exclude: - continue - - if os.path.splitext(f)[-1] in ( - ".pyc", - ".so", - ".o", - ".pyo", - ".pyd", - ".c", - ".cpp", - ".orig", - ): - self._clean_me.append(filepath) - self._clean_trees.append(pjoin(root, d) for d in dirs if d == "__pycache__") - - # clean the generated pxi files - for pxifile in _pxifiles: - pxifile_replaced = pxifile.replace(".pxi.in", ".pxi") - self._clean_me.append(pxifile_replaced) - - self._clean_trees.append(d for d in ("build", "dist") if os.path.exists(d)) - - def finalize_options(self) -> None: - pass - - def run(self) -> None: - for clean_me in self._clean_me: - try: - os.unlink(clean_me) - except OSError: - pass - for clean_tree in self._clean_trees: - try: - shutil.rmtree(clean_tree) - except OSError: - pass - - -# we need to inherit from the versioneer -# class as it encodes the version info -sdist_class = cmdclass["sdist"] - - -class CheckSDist(sdist_class): - """Custom sdist that ensures Cython has compiled all pyx files to c.""" - - _pyxfiles = [ - "pandas/_libs/arrays.pyx", - "pandas/_libs/lib.pyx", - "pandas/_libs/hashtable.pyx", - "pandas/_libs/tslib.pyx", - "pandas/_libs/index.pyx", - "pandas/_libs/internals.pyx", - "pandas/_libs/algos.pyx", - "pandas/_libs/join.pyx", - "pandas/_libs/indexing.pyx", - "pandas/_libs/interval.pyx", - "pandas/_libs/hashing.pyx", - "pandas/_libs/missing.pyx", - "pandas/_libs/testing.pyx", - "pandas/_libs/sparse.pyx", - "pandas/_libs/ops.pyx", - "pandas/_libs/parsers.pyx", - "pandas/_libs/tslibs/base.pyx", - "pandas/_libs/tslibs/ccalendar.pyx", - "pandas/_libs/tslibs/dtypes.pyx", - "pandas/_libs/tslibs/period.pyx", - "pandas/_libs/tslibs/strptime.pyx", - "pandas/_libs/tslibs/np_datetime.pyx", - "pandas/_libs/tslibs/timedeltas.pyx", - "pandas/_libs/tslibs/timestamps.pyx", - "pandas/_libs/tslibs/timezones.pyx", - "pandas/_libs/tslibs/conversion.pyx", - "pandas/_libs/tslibs/fields.pyx", - "pandas/_libs/tslibs/offsets.pyx", - "pandas/_libs/tslibs/parsing.pyx", - "pandas/_libs/tslibs/tzconversion.pyx", - "pandas/_libs/tslibs/vectorized.pyx", - "pandas/_libs/window/indexers.pyx", - "pandas/_libs/writers.pyx", - "pandas/_libs/sas.pyx", - "pandas/_libs/byteswap.pyx", - ] - - _cpp_pyxfiles = [ - "pandas/_libs/window/aggregations.pyx", - ] - - def initialize_options(self) -> None: - sdist_class.initialize_options(self) - - def run(self) -> None: - if "cython" in cmdclass: - self.run_command("cython") - else: - # If we are not running cython then - # compile the extensions correctly - pyx_files = [(self._pyxfiles, "c"), (self._cpp_pyxfiles, "cpp")] - - for pyxfiles, extension in pyx_files: - for pyxfile in pyxfiles: - sourcefile = pyxfile[:-3] + extension - msg = ( - f"{extension}-source file '{sourcefile}' not found.\n" - "Run 'setup.py cython' before sdist." - ) - assert os.path.isfile(sourcefile), msg - sdist_class.run(self) - - -class CheckingBuildExt(build_ext): - """ - Subclass build_ext to get clearer report if Cython is necessary. - """ - - def check_cython_extensions(self, extensions) -> None: - for ext in extensions: - for src in ext.sources: - if not os.path.exists(src): - print(f"{ext.name}: -> [{ext.sources}]") - raise Exception( - f"""Cython-generated file '{src}' not found. - Cython is required to compile pandas from a development branch. - Please install Cython or download a release package of pandas. - """ - ) - - def build_extensions(self) -> None: - self.check_cython_extensions(self.extensions) - build_ext.build_extensions(self) - - -class CythonCommand(build_ext): - """ - Custom command subclassed from Cython.Distutils.build_ext - to compile pyx->c, and stop there. All this does is override the - C-compile method build_extension() with a no-op. - """ - - def build_extension(self, ext) -> None: - pass - - -class DummyBuildSrc(Command): - """numpy's build_src command interferes with Cython's build_ext.""" - - user_options = [] - - def initialize_options(self) -> None: - self.py_modules_dict = {} - - def finalize_options(self) -> None: - pass - - def run(self) -> None: - pass - - -cmdclass["clean"] = CleanCommand -cmdclass["build_ext"] = CheckingBuildExt - -if _CYTHON_INSTALLED: - suffix = ".pyx" - cmdclass["cython"] = CythonCommand -else: - suffix = ".c" - cmdclass["build_src"] = DummyBuildSrc - -# ---------------------------------------------------------------------- -# Preparation of compiler arguments - -debugging_symbols_requested = "--with-debugging-symbols" in sys.argv -if debugging_symbols_requested: - sys.argv.remove("--with-debugging-symbols") - - -if sys.byteorder == "big": - endian_macro = [("__BIG_ENDIAN__", "1")] -else: - endian_macro = [("__LITTLE_ENDIAN__", "1")] - - -extra_compile_args = [] -extra_link_args = [] -if is_platform_windows(): - if debugging_symbols_requested: - extra_compile_args.append("/Z7") - extra_link_args.append("/DEBUG") -else: - # PANDAS_CI=1 is set in CI - if os.environ.get("PANDAS_CI", "0") == "1": - extra_compile_args.append("-Werror") - if debugging_symbols_requested: - extra_compile_args.append("-g3") - extra_compile_args.append("-UNDEBUG") - extra_compile_args.append("-O0") - -# Build for at least macOS 10.9 when compiling on a 10.9 system or above, -# overriding CPython distuitls behaviour which is to target the version that -# python was built for. This may be overridden by setting -# MACOSX_DEPLOYMENT_TARGET before calling setup.py -if is_platform_mac(): - if "MACOSX_DEPLOYMENT_TARGET" not in os.environ: - current_system = platform.mac_ver()[0] - python_target = get_config_vars().get( - "MACOSX_DEPLOYMENT_TARGET", current_system - ) - target_macos_version = "10.9" - parsed_macos_version = parse_version(target_macos_version) - if ( - parse_version(str(python_target)) - < parsed_macos_version - <= parse_version(current_system) - ): - os.environ["MACOSX_DEPLOYMENT_TARGET"] = target_macos_version - - if sys.version_info[:2] == (3, 8): # GH 33239 - extra_compile_args.append("-Wno-error=deprecated-declarations") - - # https://github.com/pandas-dev/pandas/issues/35559 - extra_compile_args.append("-Wno-error=unreachable-code") - -# enable coverage by building cython files by setting the environment variable -# "PANDAS_CYTHON_COVERAGE" (with a Truthy value) or by running build_ext -# with `--with-cython-coverage`enabled -linetrace = os.environ.get("PANDAS_CYTHON_COVERAGE", False) -if "--with-cython-coverage" in sys.argv: - linetrace = True - sys.argv.remove("--with-cython-coverage") - -# Note: if not using `cythonize`, coverage can be enabled by -# pinning `ext.cython_directives = directives` to each ext in extensions. -# github.com/cython/cython/wiki/enhancements-compilerdirectives#in-setuppy -directives = {"linetrace": False, "language_level": 3, "always_allow_keywords": True} -macros = [] -if linetrace: - # https://pypkg.com/pypi/pytest-cython/f/tests/example-project/setup.py - directives["linetrace"] = True - macros = [("CYTHON_TRACE", "1"), ("CYTHON_TRACE_NOGIL", "1")] - -# silence build warnings about deprecated API usage -# we can't do anything about these warnings because they stem from -# cython+numpy version mismatches. -macros.append(("NPY_NO_DEPRECATED_API", "0")) - - -# ---------------------------------------------------------------------- -# Specification of Dependencies - - -# TODO(cython#4518): Need to check to see if e.g. `linetrace` has changed and -# possibly re-compile. -def maybe_cythonize(extensions, *args, **kwargs): - """ - Render tempita templates before calling cythonize. This is skipped for - - * clean - * sdist - """ - if "clean" in sys.argv or "sdist" in sys.argv: - # See https://github.com/cython/cython/issues/1495 - return extensions - - elif not _CYTHON_INSTALLED: - # GH#28836 raise a helfpul error message - if _CYTHON_VERSION: - raise RuntimeError( - f"Cannot cythonize with old Cython version ({_CYTHON_VERSION} " - f"installed, needs {min_cython_ver})" - ) - raise RuntimeError("Cannot cythonize without Cython installed.") - - # reuse any parallel arguments provided for compilation to cythonize - parser = argparse.ArgumentParser() - parser.add_argument("--parallel", "-j", type=int, default=1) - parsed, _ = parser.parse_known_args() - - kwargs["nthreads"] = parsed.parallel - build_ext.render_templates(_pxifiles) - if debugging_symbols_requested: - kwargs["gdb_debug"] = True - - return cythonize(extensions, *args, **kwargs) - - -def srcpath(name=None, suffix=".pyx", subdir="src"): - return pjoin("pandas", subdir, name + suffix) - - -lib_depends = ["pandas/_libs/include/pandas/parse_helper.h"] - -tseries_depends = [ - "pandas/_libs/include/pandas/datetime/pd_datetime.h", -] - -ext_data = { - "_libs.algos": { - "pyxfile": "_libs/algos", - "depends": _pxi_dep["algos"], - }, - "_libs.arrays": {"pyxfile": "_libs/arrays"}, - "_libs.groupby": {"pyxfile": "_libs/groupby"}, - "_libs.hashing": {"pyxfile": "_libs/hashing", "depends": []}, - "_libs.hashtable": { - "pyxfile": "_libs/hashtable", - "depends": ( - [ - "pandas/_libs/include/pandas/vendored/klib/khash_python.h", - "pandas/_libs/include/pandas/vendored/klib/khash.h", - ] - + _pxi_dep["hashtable"] - ), - }, - "_libs.index": { - "pyxfile": "_libs/index", - "depends": _pxi_dep["index"], - }, - "_libs.indexing": {"pyxfile": "_libs/indexing"}, - "_libs.internals": {"pyxfile": "_libs/internals"}, - "_libs.interval": { - "pyxfile": "_libs/interval", - "depends": _pxi_dep["interval"], - }, - "_libs.join": {"pyxfile": "_libs/join"}, - "_libs.lib": { - "pyxfile": "_libs/lib", - "depends": lib_depends + tseries_depends, - }, - "_libs.missing": {"pyxfile": "_libs/missing", "depends": tseries_depends}, - "_libs.parsers": { - "pyxfile": "_libs/parsers", - "depends": [ - "pandas/_libs/src/parser/tokenizer.h", - "pandas/_libs/src/parser/io.h", - "pandas/_libs/src/pd_parser.h", - ], - }, - "_libs.ops": {"pyxfile": "_libs/ops"}, - "_libs.ops_dispatch": {"pyxfile": "_libs/ops_dispatch"}, - "_libs.properties": {"pyxfile": "_libs/properties"}, - "_libs.reshape": {"pyxfile": "_libs/reshape", "depends": []}, - "_libs.sparse": {"pyxfile": "_libs/sparse", "depends": _pxi_dep["sparse"]}, - "_libs.tslib": { - "pyxfile": "_libs/tslib", - "depends": tseries_depends, - }, - "_libs.tslibs.base": {"pyxfile": "_libs/tslibs/base"}, - "_libs.tslibs.ccalendar": {"pyxfile": "_libs/tslibs/ccalendar"}, - "_libs.tslibs.dtypes": {"pyxfile": "_libs/tslibs/dtypes"}, - "_libs.tslibs.conversion": { - "pyxfile": "_libs/tslibs/conversion", - "depends": tseries_depends, - }, - "_libs.tslibs.fields": { - "pyxfile": "_libs/tslibs/fields", - "depends": tseries_depends, - }, - "_libs.tslibs.nattype": {"pyxfile": "_libs/tslibs/nattype"}, - "_libs.tslibs.np_datetime": { - "pyxfile": "_libs/tslibs/np_datetime", - "depends": tseries_depends, - }, - "_libs.tslibs.offsets": { - "pyxfile": "_libs/tslibs/offsets", - "depends": tseries_depends, - }, - "_libs.tslibs.parsing": { - "pyxfile": "_libs/tslibs/parsing", - "sources": ["pandas/_libs/src/parser/tokenizer.c"], - }, - "_libs.tslibs.period": { - "pyxfile": "_libs/tslibs/period", - "depends": tseries_depends, - }, - "_libs.tslibs.strptime": { - "pyxfile": "_libs/tslibs/strptime", - "depends": tseries_depends, - }, - "_libs.tslibs.timedeltas": { - "pyxfile": "_libs/tslibs/timedeltas", - "depends": tseries_depends, - }, - "_libs.tslibs.timestamps": { - "pyxfile": "_libs/tslibs/timestamps", - "depends": tseries_depends, - }, - "_libs.tslibs.timezones": {"pyxfile": "_libs/tslibs/timezones"}, - "_libs.tslibs.tzconversion": { - "pyxfile": "_libs/tslibs/tzconversion", - "depends": tseries_depends, - }, - "_libs.tslibs.vectorized": { - "pyxfile": "_libs/tslibs/vectorized", - "depends": tseries_depends, - }, - "_libs.testing": {"pyxfile": "_libs/testing"}, - "_libs.window.aggregations": { - "pyxfile": "_libs/window/aggregations", - "language": "c++", - "suffix": ".cpp", - "depends": ["pandas/_libs/include/pandas/skiplist.h"], - }, - "_libs.window.indexers": {"pyxfile": "_libs/window/indexers"}, - "_libs.writers": {"pyxfile": "_libs/writers"}, - "_libs.sas": {"pyxfile": "_libs/sas"}, - "_libs.byteswap": {"pyxfile": "_libs/byteswap"}, -} - -extensions = [] - -for name, data in ext_data.items(): - source_suffix = suffix if suffix == ".pyx" else data.get("suffix", ".c") - - sources = [srcpath(data["pyxfile"], suffix=source_suffix, subdir="")] - - sources.extend(data.get("sources", [])) - - include = ["pandas/_libs/include", numpy.get_include()] - - undef_macros = [] - - if ( - sys.platform == "zos" - and data.get("language") == "c++" - and os.path.basename(os.environ.get("CXX", "/bin/xlc++")) in ("xlc", "xlc++") - ): - data.get("macros", macros).append(("__s390__", "1")) - extra_compile_args.append("-qlanglvl=extended0x:nolibext") - undef_macros.append("_POSIX_THREADS") - - obj = Extension( - f"pandas.{name}", - sources=sources, - depends=data.get("depends", []), - include_dirs=include, - language=data.get("language", "c"), - define_macros=data.get("macros", macros), - extra_compile_args=extra_compile_args, - extra_link_args=extra_link_args, - undef_macros=undef_macros, - ) - - extensions.append(obj) - -# ---------------------------------------------------------------------- -# ujson - -if suffix == ".pyx": - # undo dumb setuptools bug clobbering .pyx sources back to .c - for ext in extensions: - if ext.sources[0].endswith((".c", ".cpp")): - root, _ = os.path.splitext(ext.sources[0]) - ext.sources[0] = root + suffix - -ujson_ext = Extension( - "pandas._libs.json", - depends=[ - "pandas/_libs/include/pandas/vendored/ujson/lib/ultrajson.h", - "pandas/_libs/include/pandas/datetime/pd_datetime.h", - ], - sources=( - [ - "pandas/_libs/src/vendored/ujson/python/ujson.c", - "pandas/_libs/src/vendored/ujson/python/objToJSON.c", - "pandas/_libs/src/vendored/ujson/python/JSONtoObj.c", - "pandas/_libs/src/vendored/ujson/lib/ultrajsonenc.c", - "pandas/_libs/src/vendored/ujson/lib/ultrajsondec.c", - ] - ), - include_dirs=[ - "pandas/_libs/include", - numpy.get_include(), - ], - extra_compile_args=(extra_compile_args), - extra_link_args=extra_link_args, - define_macros=macros, -) - - -extensions.append(ujson_ext) - -# ---------------------------------------------------------------------- - -# ---------------------------------------------------------------------- -# pd_datetime -pd_dt_ext = Extension( - "pandas._libs.pandas_datetime", - depends=["pandas/_libs/tslibs/datetime/pd_datetime.h"], - sources=( - [ - "pandas/_libs/src/vendored/numpy/datetime/np_datetime.c", - "pandas/_libs/src/vendored/numpy/datetime/np_datetime_strings.c", - "pandas/_libs/src/datetime/date_conversions.c", - "pandas/_libs/src/datetime/pd_datetime.c", - ] - ), - include_dirs=[ - "pandas/_libs/include", - numpy.get_include(), - ], - extra_compile_args=(extra_compile_args), - extra_link_args=extra_link_args, - define_macros=macros, -) - - -extensions.append(pd_dt_ext) - -# ---------------------------------------------------------------------- - -# ---------------------------------------------------------------------- -# pd_datetime -pd_parser_ext = Extension( - "pandas._libs.pandas_parser", - depends=["pandas/_libs/include/pandas/parser/pd_parser.h"], - sources=( - [ - "pandas/_libs/src/parser/tokenizer.c", - "pandas/_libs/src/parser/io.c", - "pandas/_libs/src/parser/pd_parser.c", - ] - ), - include_dirs=[ - "pandas/_libs/include", - ], - extra_compile_args=(extra_compile_args), - extra_link_args=extra_link_args, - define_macros=macros, -) - - -extensions.append(pd_parser_ext) - - -# ---------------------------------------------------------------------- - - -if __name__ == "__main__": - # Freeze to support parallel compilation when using spawn instead of fork - multiprocessing.freeze_support() - setup( - version=versioneer.get_version(), - ext_modules=maybe_cythonize(extensions, compiler_directives=directives), - cmdclass=cmdclass, - ) From 413b5721b3eefcdc0c536a315bb1e5935a431f44 Mon Sep 17 00:00:00 2001 From: Will Ayd Date: Thu, 21 Mar 2024 14:58:48 -0400 Subject: [PATCH 11/17] pyright fixes --- pandas/__init__.py | 6 +++--- pandas/_build_utils/gitversion.py | 25 ++++++++++++------------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/pandas/__init__.py b/pandas/__init__.py index 7dd02bb9fea5b..b743653830dfa 100644 --- a/pandas/__init__.py +++ b/pandas/__init__.py @@ -173,10 +173,10 @@ # If a version with git hash was stored, use that instead _built_with_meson = True -from . import _version +from pandas._version import git_revision # pyright: ignore [reportMissingImports] -__version__ = _version.__version__ -__git_version__ = _version.git_revision +__git_version__ = git_revision +del git_revision # module level doc-string diff --git a/pandas/_build_utils/gitversion.py b/pandas/_build_utils/gitversion.py index 6ff653f7db4e3..320bdf1e7cc56 100755 --- a/pandas/_build_utils/gitversion.py +++ b/pandas/_build_utils/gitversion.py @@ -3,30 +3,29 @@ # This file is vendored from NumPy. See NUMPY_LICENSE.txt import os +import os.path +import subprocess import textwrap def init_version() -> str: init = os.path.join(os.path.dirname(__file__), "../../pyproject.toml") - with open(init) as fid: + with open(init, encoding="utf-8") as fid: data = fid.readlines() version_line = next(line for line in data if line.startswith("version =")) - version = version_line.strip().split(" = ")[1] - version = version.replace('"', "").replace("'", "") + result = version_line.strip().split(" = ")[1] + result = result.replace('"', "").replace("'", "") - return version + return result def git_version(version: str) -> tuple[str, str]: # Append last commit date and hash to dev version information, # if available - import os.path - import subprocess - - git_hash = "" + ghash = "" try: p = subprocess.Popen( ["git", "log", "-1", '--format="%H %aI"'], @@ -39,20 +38,20 @@ def git_version(version: str) -> tuple[str, str]: else: out, err = p.communicate() if p.returncode == 0: - git_hash, git_date = ( + ghash, git_date = ( out.decode("utf-8") .strip() .replace('"', "") - .split("T")[0] + .split("T", maxsplit=1)[0] .replace("-", "") .split() ) # Only attach git tag to development versions if "dev" in version: - version += f"+git{git_date}.{git_hash[:7]}" + version += f"+git{git_date}.{ghash[:7]}" - return version, git_hash + return version, ghash if __name__ == "__main__": @@ -85,7 +84,7 @@ def git_version(version: str) -> tuple[str, str]: if relpath.startswith("."): relpath = outfile - with open(outfile, "w") as f: + with open(outfile, "w", encoding="utf-8") as f: print(f"Saving version to {relpath}") f.write(template) else: From 937afb552d8dbb35639ecec0c6e080b34dfdc168 Mon Sep 17 00:00:00 2001 From: Will Ayd Date: Thu, 21 Mar 2024 16:29:50 -0400 Subject: [PATCH 12/17] Fix import --- pandas/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pandas/__init__.py b/pandas/__init__.py index b743653830dfa..0b17f7e2222d0 100644 --- a/pandas/__init__.py +++ b/pandas/__init__.py @@ -173,8 +173,12 @@ # If a version with git hash was stored, use that instead _built_with_meson = True -from pandas._version import git_revision # pyright: ignore [reportMissingImports] +from pandas._version import ( # pyright: ignore [reportMissingImports] + __version__ as generated_ver, + git_revision, +) +__version__ = generated_ver __git_version__ = git_revision del git_revision From d698f8721ac4b31a281165f02078e91207754232 Mon Sep 17 00:00:00 2001 From: Will Ayd Date: Thu, 21 Mar 2024 16:31:39 -0400 Subject: [PATCH 13/17] Remove _built_with_meson --- doc/source/development/contributing_environment.rst | 5 ----- pandas/__init__.py | 1 - pandas/tests/api/test_api.py | 4 +--- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/doc/source/development/contributing_environment.rst b/doc/source/development/contributing_environment.rst index 325c902dd4f9e..deb30b62ce2e6 100644 --- a/doc/source/development/contributing_environment.rst +++ b/doc/source/development/contributing_environment.rst @@ -303,8 +303,3 @@ output when importing pandas, you can set the environment variable ``MESONPY_EDT If you would like to see this verbose output every time, you can set the ``editable-verbose`` config setting to ``true`` like so:: python -m pip install -ve . --config-settings editable-verbose=true - -.. tip:: - If you ever find yourself wondering whether setuptools or meson was used to build your pandas, - you can check the value of ``pandas._built_with_meson``, which will be true if meson was used - to compile pandas. diff --git a/pandas/__init__.py b/pandas/__init__.py index 0b17f7e2222d0..0ad58985ab04c 100644 --- a/pandas/__init__.py +++ b/pandas/__init__.py @@ -172,7 +172,6 @@ from pandas.util._tester import test # If a version with git hash was stored, use that instead -_built_with_meson = True from pandas._version import ( # pyright: ignore [reportMissingImports] __version__ as generated_ver, git_revision, diff --git a/pandas/tests/api/test_api.py b/pandas/tests/api/test_api.py index ce028a2fc4675..ed0dc219c1e6c 100644 --- a/pandas/tests/api/test_api.py +++ b/pandas/tests/api/test_api.py @@ -47,7 +47,7 @@ class TestPDApi(Base): "io", "tseries", ] - private_lib = ["compat", "core", "pandas", "util", "_built_with_meson"] + private_lib = ["compat", "core", "pandas", "util"] # misc misc = ["IndexSlice", "NaT", "NA"] @@ -191,8 +191,6 @@ class TestPDApi(Base): "_testing", "_typing", ] - if not pd._built_with_meson: - private_modules.append("_version") def test_api(self): checkthese = ( From 1f67ef007bea08f8017757cd2045787939efe21a Mon Sep 17 00:00:00 2001 From: Will Ayd Date: Thu, 21 Mar 2024 16:31:55 -0400 Subject: [PATCH 14/17] test_api fix --- pandas/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/__init__.py b/pandas/__init__.py index 0ad58985ab04c..9dc23eeea176a 100644 --- a/pandas/__init__.py +++ b/pandas/__init__.py @@ -179,7 +179,7 @@ __version__ = generated_ver __git_version__ = git_revision -del git_revision +del git_revision, generated_ver # module level doc-string From ab0f9daeb1195fcadca2d5b704a975510fe045dc Mon Sep 17 00:00:00 2001 From: Will Ayd Date: Thu, 21 Mar 2024 17:00:55 -0400 Subject: [PATCH 15/17] make pylint happy --- pandas/_build_utils/gitversion.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pandas/_build_utils/gitversion.py b/pandas/_build_utils/gitversion.py index 320bdf1e7cc56..9bb19beda97b8 100755 --- a/pandas/_build_utils/gitversion.py +++ b/pandas/_build_utils/gitversion.py @@ -49,9 +49,11 @@ def git_version(version: str) -> tuple[str, str]: # Only attach git tag to development versions if "dev" in version: - version += f"+git{git_date}.{ghash[:7]}" + ret_version = f"{version}+git{git_date}.{ghash[:7]}" + else: + ret_version = version - return version, ghash + return ret_version, ghash if __name__ == "__main__": From 45bd1049fa30379e0bdf1b08a656daa199ea0145 Mon Sep 17 00:00:00 2001 From: Will Ayd Date: Thu, 21 Mar 2024 17:01:39 -0400 Subject: [PATCH 16/17] fixup --- pandas/_build_utils/gitversion.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pandas/_build_utils/gitversion.py b/pandas/_build_utils/gitversion.py index 9bb19beda97b8..2b6866b86a6c5 100755 --- a/pandas/_build_utils/gitversion.py +++ b/pandas/_build_utils/gitversion.py @@ -26,6 +26,7 @@ def git_version(version: str) -> tuple[str, str]: # if available ghash = "" + ret_version = version try: p = subprocess.Popen( ["git", "log", "-1", '--format="%H %aI"'], @@ -50,8 +51,6 @@ def git_version(version: str) -> tuple[str, str]: # Only attach git tag to development versions if "dev" in version: ret_version = f"{version}+git{git_date}.{ghash[:7]}" - else: - ret_version = version return ret_version, ghash From 84fbbb0d3729a190a9f377ac6b55cfbca3eef4c0 Mon Sep 17 00:00:00 2001 From: Will Ayd Date: Thu, 21 Mar 2024 18:50:49 -0400 Subject: [PATCH 17/17] pylint fixes --- pandas/_build_utils/gitversion.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pandas/_build_utils/gitversion.py b/pandas/_build_utils/gitversion.py index 2b6866b86a6c5..b065530a484c1 100755 --- a/pandas/_build_utils/gitversion.py +++ b/pandas/_build_utils/gitversion.py @@ -21,12 +21,12 @@ def init_version() -> str: return result -def git_version(version: str) -> tuple[str, str]: +def git_version(version_str: str) -> tuple[str, str]: # Append last commit date and hash to dev version information, # if available ghash = "" - ret_version = version + result = version_str try: p = subprocess.Popen( ["git", "log", "-1", '--format="%H %aI"'], @@ -49,10 +49,10 @@ def git_version(version: str) -> tuple[str, str]: ) # Only attach git tag to development versions - if "dev" in version: - ret_version = f"{version}+git{git_date}.{ghash[:7]}" + if "dev" in version_str: + result = f"{version_str}+git{git_date}.{ghash[:7]}" - return ret_version, ghash + return result, ghash if __name__ == "__main__":