Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
7c0a8fe
Base version of automated test marker
ShaharNaveh Aug 11, 2025
69ed76b
Remove debug code
ShaharNaveh Aug 11, 2025
dc3a6de
Add newline
ShaharNaveh Aug 11, 2025
5c09ef0
Apply RustPython patch
ShaharNaveh Aug 11, 2025
ad061b2
Convert to tool with args
ShaharNaveh Aug 20, 2025
a6f2324
Apply patch
ShaharNaveh Aug 22, 2025
302b3d1
Remove old script
ShaharNaveh Aug 22, 2025
db74d2f
Add textwrap.py
ShaharNaveh Aug 22, 2025
11694e3
ruff fmt
ShaharNaveh Aug 22, 2025
d8b4e26
Add more modules
ShaharNaveh Aug 22, 2025
2308a5a
Merge remote-tracking branch 'upstream/main' into auto-updater
ShaharNaveh Aug 25, 2025
2fb6842
Add `gen` subcommand
ShaharNaveh Aug 29, 2025
db5eb4b
Use `_generate_next_value_`
ShaharNaveh Aug 29, 2025
af1c28d
Gen & patch
ShaharNaveh Aug 30, 2025
42365d2
Remove old tool
ShaharNaveh Aug 30, 2025
fdce40b
Merge remote-tracking branch 'upstream/main' into auto-updater
ShaharNaveh Aug 30, 2025
32baa80
Revert changes under `Lib/`
ShaharNaveh Aug 30, 2025
fb7324d
Don't crash if cls renamed/moved
ShaharNaveh Aug 30, 2025
f4056ac
Update `Lib/test/test_os.py` with tool
ShaharNaveh Aug 30, 2025
4296b59
apply patch
ShaharNaveh Aug 30, 2025
e2aa220
Fix double assignment
ShaharNaveh Aug 30, 2025
74b47a0
Better args
ShaharNaveh Aug 30, 2025
51c6ad9
Update `test_list.py` as well
ShaharNaveh Aug 30, 2025
01a90ef
Less complex print
ShaharNaveh Aug 30, 2025
a0e56ae
Improve exoectedFailure match
ShaharNaveh Aug 30, 2025
43a63a8
fix list slice
ShaharNaveh Aug 30, 2025
3e2c1f1
Add __doc__ and to help
ShaharNaveh Sep 2, 2025
789cf6e
Merge remote-tracking branch 'upstream/main' into auto-updater
ShaharNaveh Sep 2, 2025
3e9872c
Update scripts/lib_updater.py
ShaharNaveh Sep 4, 2025
d0763e4
Clearer output arg
ShaharNaveh Sep 5, 2025
e00bb28
Don't crash on missing id
ShaharNaveh Sep 5, 2025
8a5875e
Merge remote-tracking branch 'upstream/main' into auto-updater
ShaharNaveh Sep 5, 2025
6c615cc
Merge remote-tracking branch 'origin/auto-updater' into auto-updater
ShaharNaveh Sep 5, 2025
c0e90cc
Fix comment regex
ShaharNaveh Sep 6, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Convert to tool with args
  • Loading branch information
ShaharNaveh committed Aug 22, 2025
commit ad061b2efe0981fd87bb5cb00e882205053a8ba6
1 change: 1 addition & 0 deletions Lib/argparse.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# upstream_version: v3.13.7
# Author: Steven J. Bethard <steven.bethard@gmail.com>.
# New maintainer as of 29 August 2019: Raymond Hettinger <raymond.hettinger@gmail.com>

Expand Down
1 change: 1 addition & 0 deletions Lib/ast.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# upstream_version: v3.13.7
"""
The `ast` module helps Python applications to process trees of the Python
abstract syntax grammar. The abstract syntax itself might change with
Expand Down
1 change: 1 addition & 0 deletions Lib/decimal.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# upstream_version: v3.13.7
"""Decimal fixed-point and floating-point arithmetic.

This is an implementation of decimal floating-point arithmetic based on
Expand Down
1 change: 1 addition & 0 deletions Lib/heapq.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# upstream_version: v3.13.7
"""Heap queue algorithm (a.k.a. priority queue).

Heaps are arrays for which a[k] <= a[2*k+1] and a[k] <= a[2*k+2] for
Expand Down
1 change: 1 addition & 0 deletions Lib/ipaddress.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# upstream_version: v3.13.7
# Copyright 2007 Google Inc.
# Licensed to PSF under a Contributor Agreement.

Expand Down
12 changes: 11 additions & 1 deletion Lib/numbers.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# upstream_version: v3.13.7
# Copyright 2007 Google, Inc. All Rights Reserved.
# Licensed to PSF under a Contributor Agreement.

Expand Down Expand Up @@ -290,18 +291,27 @@ def conjugate(self):


class Rational(Real):
""".numerator and .denominator should be in lowest terms."""
"""To Real, Rational adds numerator and denominator properties.

The numerator and denominator values should be in lowest terms,
with a positive denominator.
"""

__slots__ = ()

@property
@abstractmethod
def numerator(self):
"""The numerator of a rational number in lowest terms."""
raise NotImplementedError

@property
@abstractmethod
def denominator(self):
"""The denominator of a rational number in lowest terms.

This denominator should be positive.
"""
raise NotImplementedError

# Concrete implementation of Real's conversion to float.
Expand Down
3 changes: 2 additions & 1 deletion Lib/os.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# upstream_version: v3.13.7
r"""OS routines for NT or Posix depending on what system we're on.

This exports:
Expand All @@ -10,7 +11,7 @@
- os.extsep is the extension separator (always '.')
- os.altsep is the alternate pathname separator (None or '/')
- os.pathsep is the component separator used in $PATH etc
- os.linesep is the line separator in text files ('\r' or '\n' or '\r\n')
- os.linesep is the line separator in text files ('\n' or '\r\n')
- os.defpath is the default search path for executables
- os.devnull is the file path of the null device ('/dev/null', etc.)

Expand Down
1 change: 1 addition & 0 deletions Lib/pprint.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# upstream_version: v3.13.7
# Author: Fred L. Drake, Jr.
# fdrake@acm.org
#
Expand Down
12 changes: 6 additions & 6 deletions Lib/queue.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# upstream_version: v3.13.7
'''A multi-producer, multi-consumer queue.'''

import threading
Expand Down Expand Up @@ -80,9 +81,6 @@ def task_done(self):
have been processed (meaning that a task_done() call was received
for every item that had been put() into the queue).

shutdown(immediate=True) calls task_done() for each remaining item in
the queue.

Raises a ValueError if called more times than there were items
placed in the queue.
'''
Expand Down Expand Up @@ -239,9 +237,11 @@ def shutdown(self, immediate=False):
By default, gets will only raise once the queue is empty. Set
'immediate' to True to make gets raise immediately instead.

All blocked callers of put() and get() will be unblocked. If
'immediate', a task is marked as done for each item remaining in
the queue, which may unblock callers of join().
All blocked callers of put() and get() will be unblocked.

If 'immediate', the queue is drained and unfinished tasks
is reduced by the number of drained tasks. If unfinished tasks
is reduced to zero, callers of Queue.join are unblocked.
'''
with self.mutex:
self.is_shutdown = True
Expand Down
5 changes: 2 additions & 3 deletions Lib/test/test_os.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# upstream_version: v3.13.7
# As a test suite for the os module, this is woefully inadequate, but this
# does add tests for a few functions which have been determined to be more
# portable than they had been thought to be.
Expand Down Expand Up @@ -829,9 +830,7 @@ def ns_to_sec(ns):
# Convert a number of nanosecond (int) to a number of seconds (float).
# Round towards infinity by adding 0.5 nanosecond to avoid rounding
# issue, os.utime() rounds towards minus infinity.
# XXX: RUSTPYTHON os.utime() use `[Duration::from_secs_f64](https://doc.rust-lang.org/std/time/struct.Duration.html#method.try_from_secs_f64)`
Copy link
Copy Markdown
Contributor Author

@ShaharNaveh ShaharNaveh Aug 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had this crazy idea to store patch files in the repo for patches like this. similar to what Termux is doing for their packages, for example:
https://github.com/termux/termux-packages/blob/9865d958666e04f16201a0774a646f37b6082c80/packages/python/0001-fix-hardcoded-paths.patch


That's probably out of scope for this PR tho

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw, your idea is not so crazy, C is NOT Rust after all, it might be needed, but yeah, totally out-of-scope for this PR.

what about an empty patch folder for now? TL;DR 🤔 I think patches for stuff like this might be the right idea, git can even generate them for us, especially if it we can make them configureable (knobs like: os/arch pair, patch-id, cpython version, and context lines needed (for preventing regressions in the future, etc.) come to mind) for now we could just have a patch folder (empty, but as a placeholder)

# return (ns * 1e-9) + 0.5e-9
return (ns * 1e-9)
return (ns * 1e-9) + 0.5e-9

def test_utime_by_indexed(self):
# pass times as floating-point seconds as the second indexed parameter
Expand Down
1 change: 1 addition & 0 deletions Lib/this.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# upstream_version: v3.13.7
s = """Gur Mra bs Clguba, ol Gvz Crgref

Ornhgvshy vf orggre guna htyl.
Expand Down
1 change: 1 addition & 0 deletions tools/rpau/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# **R**ust**P**ython **A**utomatic **U**pdater (rpau)
1 change: 1 addition & 0 deletions tools/rpau/confs/Lib/argparse.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
path = "Lib/argparse.py"
1 change: 1 addition & 0 deletions tools/rpau/confs/Lib/ast.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
path = "Lib/ast.py"
1 change: 1 addition & 0 deletions tools/rpau/confs/Lib/decimal.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
path = "Lib/decimal.py"
1 change: 1 addition & 0 deletions tools/rpau/confs/Lib/heapq.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
path = "Lib/heapq.py"
1 change: 1 addition & 0 deletions tools/rpau/confs/Lib/ipaddress.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
path = "Lib/ipaddress.py"
1 change: 1 addition & 0 deletions tools/rpau/confs/Lib/numbers.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
path = "Lib/numbers.py"
1 change: 1 addition & 0 deletions tools/rpau/confs/Lib/os.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
path = "Lib/os.py"
1 change: 1 addition & 0 deletions tools/rpau/confs/Lib/pprint.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
path = "Lib/pprint.py"
1 change: 1 addition & 0 deletions tools/rpau/confs/Lib/queue.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
path = "Lib/queue.py"
1 change: 1 addition & 0 deletions tools/rpau/confs/Lib/this.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
path = "Lib/this.py"
2 changes: 1 addition & 1 deletion tools/test_marker/main.py → tools/rpau/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

COL_OFFSET = 4
INDENT1 = " " * COL_OFFSET
INDENT2 = " " * COL_OFFSET * 2
INDENT2 = INDENT1 * 2
COMMENT = "TODO: RUSTPYTHON"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 This would be nice to have an easy override for (CI may be-able to add more details dynamically that way)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ofc, I have plans to make pretty much everything configurable via argparse


ROOT_DIR = pathlib.Path(__file__).parents[2]
Expand Down
17 changes: 17 additions & 0 deletions tools/rpau/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[project]
name = "rpau"
version = "0.1.0"
description = "Tool for automaticly update Lib & Tests from CPython"
readme = "README.md"
authors = [
{ name = "RustPython Team", email = "" }
]
requires-python = ">=3.12"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Explicit requirement here is a good improvement!

🔖 Before we forget, this sort of design decision should be documented, probably in the adjacent ./README.md

dependencies = []

[project.scripts]
rpau = "rpau:main"

[build-system]
requires = ["uv_build>=0.8.11,<0.9.0"]
build-backend = "uv_build"
75 changes: 75 additions & 0 deletions tools/rpau/src/rpau/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import functools
from concurrent.futures import ProcessPoolExecutor
from typing import TYPE_CHECKING

import tomllib

from rpau.cli import build_argparse
from rpau.logger import build_root_logger, get_logger
from rpau.logic import run
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove unused import to satisfy ruff/flake8 (F401)

get_logger is imported but not used.

Apply this diff:

-from rpau.logger import build_root_logger, get_logger
+from rpau.logger import build_root_logger
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
from rpau.cli import build_argparse
from rpau.logger import build_root_logger, get_logger
from rpau.logic import run
from rpau.cli import build_argparse
from rpau.logger import build_root_logger
from rpau.logic import run
🧰 Tools
🪛 Flake8 (7.2.0)

[error] 8-8: 'rpau.logger.get_logger' imported but unused

(F401)

🤖 Prompt for AI Agents
In tools/rpau/src/rpau/__init__.py around lines 7 to 9, the get_logger symbol is
imported but never used which triggers an F401 unused-import lint error; remove
the unused import by deleting get_logger from the import list (leave
build_argparse, build_root_logger, run intact) so the file only imports the
symbols that are actually used.


if TYPE_CHECKING:
import pathlib
import re
from collections.abc import Iterator


def iter_confs(
conf_dir: "pathlib.Path", *, include: "re.Pattern", exclude: "re.Pattern"
) -> "Iterator[pathlib.Path]":
for conf_file in conf_dir.rglob("**/*.toml"):
if not conf_file.is_file():
continue

uri = conf_file.as_uri().removeprefix("file://")
if not include.match(uri):
continue
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 NIT - consider leveraging the logger more in iter_confs

while prototyping automation, it would be helpful to have the option for a debug messages for the skipped files.

consider something like:

Suggested change
continue
if (__DEBUG__):
logger.debug(
"skipping unselected '%s'", # lazy formatting to avoid PYL-W1203
uri,
)
continue


if exclude.match(uri):
continue

yield conf_file


def main() -> None:
parser = build_argparse()
args = parser.parse_args()

logger = build_root_logger(level=args.log_level)
logger.debug(f"{args=}")

if args.cache:
logger.debug(f"Ensuring {args.cache_dir} exists")
cache_dir = args.cache_dir
cache_dir.mkdir(parents=True, exist_ok=True)
else:
cache_dir = None

conf_dir = args.conf_dir
with ProcessPoolExecutor(args.workers) as executor:
for conf_file in iter_confs(
conf_dir, include=args.include, exclude=args.exclude
):
try:
conf = tomllib.loads(conf_file.read_text())
except tomllib.TOMLDecodeError as err:
logger.warn(f"{conf_file}: {err}")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 NIT - consider logging built-in frame dump feature

consider the debug friendly style:

Suggested change
logger.warn(f"{conf_file}: {err}")
logger.warning(
"%s: %s", # lazy formatting to avoid PYL-W1203
conf_file,
err, # log as just warning level, instead of exception (error), but still detailed.
exc_info=True,
)

logging functions like warn/debug/warning/etc. take a format string already, but defer it until needed, this ironically makes f-strings into overhead (see Rationale)

(the exc_info adds the exception trace details to the message)

continue

try:
path = conf.pop("path")
except KeyError:
logger.warn(f"{conf_file}: has no 'path' key. skipping")
continue

version = conf.pop("version", args.default_version)
func = functools.partial(
run,
path=path,
conf=conf,
cache_dir=cache_dir,
version=version,
output_dir=args.output_dir,
base_upstream_url=args.base_upstream_url,
)
executor.submit(func)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to assist automation, it would be helpful to collect the results of the runs (especially when being selective with the --include and --exclude args where there is little benefit gain from the multiprocessing)

134 changes: 134 additions & 0 deletions tools/rpau/src/rpau/cli.py
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Prototype is off to a good start. 🧹 Of course it will probably need cleaned up more in the future for maintainability, but it is much better than before already.

Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import argparse
import logging
import os
import pathlib
import re
import sys


class CustomArgumentParser(argparse.ArgumentParser):
class _CustomHelpFormatter(argparse.ArgumentDefaultsHelpFormatter):
def _get_help_string(self, action):
help_msg = super()._get_help_string(action)
if action.dest != "help":
env_name = f"{self._prog}_{action.dest}".upper()
env_value = os.environ.get(env_name, "")
help_msg += f" [env: {env_name}={env_value}]"
return help_msg

def __init__(self, *, formatter_class=_CustomHelpFormatter, **kwargs):
super().__init__(formatter_class=formatter_class, **kwargs)

def _add_action(self, action):
action.default = os.environ.get(
f"{self.prog}_{action.dest}".upper(), action.default
)
return super()._add_action(action)
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Aug 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Env overrides ignore types (Path/int/bool), causing subtle bugs

Overriding defaults from env injects raw strings for every action. This breaks typed options like --cache (BooleanOptionalAction), --cache-dir (Path), --workers (int), and regex patterns (type=re.compile). Also, invalid values for --log-level won’t be validated. Coerce env strings using the action’s type (or a bool parser for BooleanOptionalAction) and validate against choices.

-    def _add_action(self, action):
-        action.default = os.environ.get(
-            f"{self.prog}_{action.dest}".upper(), action.default
-        )
-        return super()._add_action(action)
+    def _add_action(self, action):
+        env_name = f"{self.prog}_{action.dest}".upper()
+        env_val = os.environ.get(env_name, None)
+        if env_val is not None:
+            # Coerce env string to the expected type
+            if isinstance(action, argparse.BooleanOptionalAction):
+                val = env_val.lower() in {"1", "true", "yes", "on"}
+            elif action.type is not None:
+                val = action.type(env_val)
+            else:
+                val = env_val
+            # Validate against choices if provided
+            if action.choices and val not in action.choices:
+                raise argparse.ArgumentError(
+                    action, f"{env_name} must be one of {list(action.choices)}"
+                )
+            action.default = val
+        return super()._add_action(action)

If you prefer not to use isinstance(..., argparse.BooleanOptionalAction), I can inline a tiny _strtobool() helper and call it whenever action.option_strings contains both the --foo and --no-foo forms.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def _add_action(self, action):
action.default = os.environ.get(
f"{self.prog}_{action.dest}".upper(), action.default
)
return super()._add_action(action)
def _add_action(self, action):
env_name = f"{self.prog}_{action.dest}".upper()
env_val = os.environ.get(env_name, None)
if env_val is not None:
# Coerce env string to the expected type
if isinstance(action, argparse.BooleanOptionalAction):
val = env_val.lower() in {"1", "true", "yes", "on"}
elif action.type is not None:
val = action.type(env_val)
else:
val = env_val
# Validate against choices if provided
if action.choices and val not in action.choices:
raise argparse.ArgumentError(
action, f"{env_name} must be one of {list(action.choices)}"
)
action.default = val
return super()._add_action(action)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 NIT - I agree with @coderabbitai on this, if the code is going to implement a custom type-aware arg-parser anyway, might as well be consistent with the types.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@reactive-firewall Thank you for the agreement! You're absolutely right - since we're already implementing a custom argument parser with environment variable support, maintaining type consistency is crucial. The current implementation would cause silent bugs where boolean flags become strings, integer workers become strings, and Path objects become strings, which could lead to confusing runtime errors downstream. The type-aware coercion ensures the environment variables work exactly as if they were passed via command line arguments.



def get_cache_dir(prog: str) -> pathlib.Path:
home = pathlib.Path.home()

if sys.platform.startswith("win"):
local_appdata = pathlib.Path(
os.getenv("LOCALAPPDATA", home / "AppData" / "Local")
)
path = local_appdata / prog / prog / "Cache"
elif sys.platform == "darwin":
path = home / "Library" / "Caches" / prog
else:
cache_home = pathlib.Path(os.getenv("XDG_CACHE_HOME", home / ".cache"))
path = cache_home / prog

return path


def build_argparse() -> argparse.ArgumentParser:
parser = CustomArgumentParser(
prog="rpau", description="Automatic update code from CPython"
)

# Cache
cache_group = parser.add_argument_group("Cache options")

cache_group.add_argument(
"--cache",
action=argparse.BooleanOptionalAction,
default=True,
help="Whether reading from or writing to the cache is allowed",
)
cache_group.add_argument(
"--cache-dir",
default=get_cache_dir(parser.prog),
help="Path to the cache directory",
metavar="PATH",
type=pathlib.Path,
)

# Output
output_group = parser.add_argument_group("Output option")

output_group.add_argument(
"-o",
"--output-dir",
default=pathlib.Path(__file__).parents[4],
help="Output dir",
metavar="PATH",
type=pathlib.Path,
)

# Filter
filter_group = parser.add_argument_group("Filter options")

filter_group.add_argument(
"-i",
"--include",
default=".*",
help="RE pattern used to include files and/or directories",
metavar="PATTERN",
type=re.compile,
)
filter_group.add_argument(
"-e",
"--exclude",
default="^$",
help="RE pattern used to omit files and/or directories",
metavar="PATTERN",
type=re.compile,
)

# Global
global_group = parser.add_argument_group("Global options")

global_group.add_argument(
"--log-level",
choices=logging.getLevelNamesMapping(),
default="WARNING",
help="Log level",
)

global_group.add_argument(
"-j", "--workers", default=1, help="Number of processes", type=int
)
global_group.add_argument(
"-c",
"--conf-dir",
default=pathlib.Path(__file__).parents[2] / "confs",
help="Path to conf dir",
metavar="PATH",
type=pathlib.Path,
)
global_group.add_argument(
"--base-upstream-url",
default="https://raw.githubusercontent.com/python/cpython/refs/tags",
help="Base upstream url of CPython",
metavar="URL",
)
global_group.add_argument(
"--default-version",
default="v3.13.7",
help="Fallback version of cpython if none specified in conf",
metavar="VERSION_TAG",
)

return parser
Loading