From af5519cb5e4fc15df4ae97b46275105d7ead1eac Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Thu, 3 Jul 2025 12:01:36 +0200 Subject: [PATCH 1/8] Adds pyproject.toml --- pyproject.toml | 49 ++++++++++++ setup.py | 211 +++++++++++++++++++++---------------------------- 2 files changed, 137 insertions(+), 123 deletions(-) create mode 100644 pyproject.toml diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..2ee73eab --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,49 @@ +[build-system] +requires = ["setuptools>=50"] +build-backend = "setuptools.build_meta" + +[project] +name = "ConfigArgParse" +version = "1.7.1" +description = "A drop-in replacement for argparse that allows options to also be set via config files and/or environment variables." +readme = "README.md" +license = {text = "MIT"} +requires-python = ">=3.6" +keywords = [ + "options", "argparse", "ConfigArgParse", "config", "environment variables", + "envvars", "ENV", "environment", "optparse", "YAML", "INI" +] +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Natural Language :: English", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy" +] +dependencies = [] + +[project.urls] +Homepage = "https://github.com/bw2/ConfigArgParse" + +[project.optional-dependencies] +yaml = ["PyYAML"] +test = [ + "mock", + "PyYAML", + "pytest", + "pytest-cov", +] + +[tool.setuptools] +py-modules = ["configargparse"] +include-package-data = true \ No newline at end of file diff --git a/setup.py b/setup.py index 3526ca51..06494341 100644 --- a/setup.py +++ b/setup.py @@ -1,128 +1,93 @@ -import logging -import os -import sys - - -try: - from setuptools import setup -except ImportError: - print("WARNING: setuptools not installed. Will try using distutils instead..") - from distutils.core import setup - - -def launch_http_server(directory): - assert os.path.isdir(directory) - - try: - try: - from SimpleHTTPServer import SimpleHTTPRequestHandler - except ImportError: - from http.server import SimpleHTTPRequestHandler - - try: - import SocketServer - except ImportError: - import socketserver as SocketServer - - import socket - - for port in [80] + list(range(8000, 8100)): +# TODO: Delete this file after we remove python 3.6 support and use pyproject.toml directly +from setuptools import setup +import ast +import re + + +def parse_toml_min(filename): + """ + Hack to parse pyproject.toml and not duplicate the data inside it. + """ + def set_in_tree(tree, path, k, v): + d = tree + for p in path: + d = d.setdefault(p, {}) + d[k] = v + + def parse_value(val): + val = val.strip() + if val == "true": + return True + if val == "false": + return False + if val == "[]": + return [] + if val.startswith("[") and val.endswith("]"): try: - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.bind(("localhost", port)) - s.close() - except socket.error as e: - logging.debug("Can't use port %d: %s" % (port, e.strerror)) - continue - - print( - "HTML coverage report now available at http://{}{}".format( - socket.gethostname(), (":%s" % port) if port != 80 else "" - ) - ) - - os.chdir(directory) - SocketServer.TCPServer(("", port), SimpleHTTPRequestHandler).serve_forever() - else: - logging.debug("All network port. ") - except Exception as e: - logging.error( - "ERROR: while starting an HTTP server to serve " - "the coverage report: %s" % e - ) - - -command = sys.argv[-1] -if command == "publish": - os.system("rm -rf dist") - os.system("python3 setup.py sdist") - os.system("python3 setup.py bdist_wheel") - os.system( - "twine check dist/*" - ) # check for formatting or other issues that would cause twine upload to error out - os.system("twine upload dist/*whl dist/*gz") - sys.exit() -elif command == "coverage": - try: - import coverage - except: - sys.exit("coverage.py not installed (pip install --user coverage)") - setup_py_path = os.path.abspath(__file__) - os.system("coverage run --source=configargparse " + setup_py_path + " test") - os.system("coverage report") - os.system("coverage html") - print("Done computing coverage") - launch_http_server(directory="htmlcov") - sys.exit() - -long_description = "" -if command not in ["test", "coverage"]: - long_description = open("README.rst").read() - -install_requires = [] -tests_require = [ - "mock", - "PyYAML", - "pytest", - "pytest-cov", -] - + return ast.literal_eval(val) + except Exception: + pass + if val.startswith("{") and val.endswith("}"): + # TOML inline tables use = not : so replace only at key boundaries + s = re.sub(r'(\w+)\s*=', r'"\1":', val) + try: + return ast.literal_eval(s) + except Exception: + pass + # If it's quoted, remove quotes + if (val.startswith('"') and val.endswith('"')) or (val.startswith("'") and val.endswith("'")): + return val[1:-1] + # Try int/float/bare string + try: + return ast.literal_eval(val) + except Exception: + return val + + tree = {} + cur = [] + with open(filename) as f: + lines = list(f) + i = 0 + while i < len(lines): + line = lines[i].strip() + i += 1 + if not line or line.startswith("#"): + continue + m = re.match(r"\[(.+)\]", line) + if m: + cur = m.group(1).split(".") + continue + if "=" in line: + k, v = map(str.strip, line.split("=", 1)) + # Handle multi-line array + if v.startswith("[") and not v.endswith("]"): + array_lines = [v] + while i < len(lines): + next_line = lines[i].strip() + array_lines.append(next_line) + i += 1 + if next_line.endswith("]"): + break + v = " ".join(array_lines) + set_in_tree(tree, cur, k, parse_value(v)) + return tree + + +toml_data = parse_toml_min("pyproject.toml") setup( - name="ConfigArgParse", - version="1.7.1", - description="A drop-in replacement for argparse that allows options to " - "also be set via config files and/or environment variables.", - long_description=long_description, - url="https://github.com/bw2/ConfigArgParse", - py_modules=["configargparse"], - include_package_data=True, - license="MIT", - keywords="options, argparse, ConfigArgParse, config, environment variables, " - "envvars, ENV, environment, optparse, YAML, INI", - classifiers=[ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Natural Language :: English", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", - "Programming Language :: Python :: Implementation :: CPython", - "Programming Language :: Python :: Implementation :: PyPy", - ], + name=toml_data["project"]["name"], + version=toml_data["project"]["version"], + description=toml_data["project"]["description"], + long_description=toml_data["project"]["readme"], + url=toml_data["project"]["urls"]["Homepage"], + py_modules=toml_data["tool"]["setuptools"]["py-modules"], + include_package_data=toml_data["tool"]["setuptools"]["include-package-data"], + license=toml_data["project"]["license"]["text"], + keywords=" ".join(toml_data["project"]["keywords"]), + classifiers=toml_data["project"]["classifiers"], test_suite="tests", - python_requires=">=3.6", - install_requires=install_requires, - tests_require=tests_require, - extras_require={ - "yaml": ["PyYAML"], - "test": tests_require, - }, + python_requires=toml_data["project"]["requires-python"], + tests_require=toml_data["project"]["optional-dependencies"]["test"], + extras_require=toml_data["project"]["optional-dependencies"], ) From e58c6914103337b1f64b8f4813850981766e8d2d Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Thu, 3 Jul 2025 12:10:11 +0200 Subject: [PATCH 2/8] . --- pyproject.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 2ee73eab..cc93e37f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,7 +38,9 @@ Homepage = "https://github.com/bw2/ConfigArgParse" [project.optional-dependencies] yaml = ["PyYAML"] test = [ + "black", "mock", + "toml", "PyYAML", "pytest", "pytest-cov", From 1ad967de855aceb17499588aac54d678a937fe04 Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Thu, 3 Jul 2025 12:10:45 +0200 Subject: [PATCH 3/8] black --- setup.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 06494341..81e59f0b 100644 --- a/setup.py +++ b/setup.py @@ -8,6 +8,7 @@ def parse_toml_min(filename): """ Hack to parse pyproject.toml and not duplicate the data inside it. """ + def set_in_tree(tree, path, k, v): d = tree for p in path: @@ -29,13 +30,15 @@ def parse_value(val): pass if val.startswith("{") and val.endswith("}"): # TOML inline tables use = not : so replace only at key boundaries - s = re.sub(r'(\w+)\s*=', r'"\1":', val) + s = re.sub(r"(\w+)\s*=", r'"\1":', val) try: return ast.literal_eval(s) except Exception: pass # If it's quoted, remove quotes - if (val.startswith('"') and val.endswith('"')) or (val.startswith("'") and val.endswith("'")): + if (val.startswith('"') and val.endswith('"')) or ( + val.startswith("'") and val.endswith("'") + ): return val[1:-1] # Try int/float/bare string try: From c104ac1d8ed2cf32bfe25d54b19bcd27f8e4c967 Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Thu, 3 Jul 2025 12:19:08 +0200 Subject: [PATCH 4/8] Better control of closing files and not just remain them open --- pyproject.toml | 4 ++-- setup.py | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index cc93e37f..b4a87713 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" name = "ConfigArgParse" version = "1.7.1" description = "A drop-in replacement for argparse that allows options to also be set via config files and/or environment variables." -readme = "README.md" +readme = "README.rst" license = {text = "MIT"} requires-python = ">=3.6" keywords = [ @@ -48,4 +48,4 @@ test = [ [tool.setuptools] py-modules = ["configargparse"] -include-package-data = true \ No newline at end of file +include-package-data = true diff --git a/setup.py b/setup.py index 81e59f0b..ba019189 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,8 @@ # TODO: Delete this file after we remove python 3.6 support and use pyproject.toml directly -from setuptools import setup import ast import re +from contextlib import closing +from setuptools import setup def parse_toml_min(filename): @@ -82,7 +83,7 @@ def parse_value(val): name=toml_data["project"]["name"], version=toml_data["project"]["version"], description=toml_data["project"]["description"], - long_description=toml_data["project"]["readme"], + long_description=closing(open(toml_data["project"]["readme"])).__enter__().read(), url=toml_data["project"]["urls"]["Homepage"], py_modules=toml_data["tool"]["setuptools"]["py-modules"], include_package_data=toml_data["tool"]["setuptools"]["include-package-data"], From d9b94a93ce5f7bad104ab3597f41f7ef02e8d9bc Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Thu, 3 Jul 2025 12:26:22 +0200 Subject: [PATCH 5/8] Sync --- pyproject.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b4a87713..b71b91a6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,8 +10,7 @@ readme = "README.rst" license = {text = "MIT"} requires-python = ">=3.6" keywords = [ - "options", "argparse", "ConfigArgParse", "config", "environment variables", - "envvars", "ENV", "environment", "optparse", "YAML", "INI" + "options", "argparse", "ConfigArgParse", "config", "environment", "variables" ] classifiers = [ "Development Status :: 4 - Beta", From c05b636bd82e4716847a241b91edc2773ed75756 Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Thu, 3 Jul 2025 12:28:03 +0200 Subject: [PATCH 6/8] ugh --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index b71b91a6..0cbd1edc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,8 @@ readme = "README.rst" license = {text = "MIT"} requires-python = ">=3.6" keywords = [ - "options", "argparse", "ConfigArgParse", "config", "environment", "variables" + "options", "argparse", "ConfigArgParse", "config", "environment variables", + "envvars", "ENV", "environment", "optparse", "YAML", "INI" ] classifiers = [ "Development Status :: 4 - Beta", From 97e3033be3a5b1640d6bcbe0f8f4402f739155b2 Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Thu, 3 Jul 2025 12:28:46 +0200 Subject: [PATCH 7/8] . --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 0cbd1edc..b4a87713 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ readme = "README.rst" license = {text = "MIT"} requires-python = ">=3.6" keywords = [ - "options", "argparse", "ConfigArgParse", "config", "environment variables", + "options", "argparse", "ConfigArgParse", "config", "environment variables", "envvars", "ENV", "environment", "optparse", "YAML", "INI" ] classifiers = [ From 2e5f9c0712ab997ae467243f7b0704f1a8c3eba3 Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Thu, 3 Jul 2025 12:31:36 +0200 Subject: [PATCH 8/8] Slight sync with tbooth's pr --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index b4a87713..fb2aeccd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools>=50"] +requires = ["setuptools>=41"] build-backend = "setuptools.build_meta" [project]