From cdfdc2feb37a712e8becac45a7eac60d84bedd71 Mon Sep 17 00:00:00 2001 From: Cristian Le Date: Tue, 4 Jul 2023 09:02:21 +0200 Subject: [PATCH 1/6] Remove travis Signed-off-by: Cristian Le --- .travis.yml | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 595e383..0000000 --- a/.travis.yml +++ /dev/null @@ -1,10 +0,0 @@ -language: python - -python: - - "3.5" - - "3.6" - - "3.7" - - "3.8" - - "3.9" - -script: test/runtests.sh From de95d2a1112fd7636553493ec3b0b092c25fb05b Mon Sep 17 00:00:00 2001 From: Cristian Le Date: Tue, 4 Jul 2023 09:00:25 +0200 Subject: [PATCH 2/6] PEP621 Signed-off-by: Cristian Le --- .readthedocs.yaml | 4 ++- MANIFEST.in | 6 ---- docs/requirements.in | 2 -- docs/requirements.txt | 57 ------------------------------ pyproject.toml | 80 +++++++++++++++++++++++++++++++++++++++++++ setup.py | 54 ----------------------------- test/runtests.sh | 38 -------------------- tox.ini | 10 ------ 8 files changed, 83 insertions(+), 168 deletions(-) delete mode 100644 MANIFEST.in delete mode 100644 docs/requirements.in delete mode 100644 docs/requirements.txt create mode 100644 pyproject.toml delete mode 100644 setup.py delete mode 100755 test/runtests.sh delete mode 100644 tox.ini diff --git a/.readthedocs.yaml b/.readthedocs.yaml index c85c8d8..2f96010 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -18,4 +18,6 @@ sphinx: # https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html python: install: - - requirements: docs/requirements.txt + - path: . + extra_requirements: + - docs diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index ed630c2..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,6 +0,0 @@ -include bin/fypp -include LICENSE.txt -include CHANGELOG.rst -recursive-include test *.sh *.inc *.py -global-exclude *.pyc -global-exclude *~ diff --git a/docs/requirements.in b/docs/requirements.in deleted file mode 100644 index 7c475e9..0000000 --- a/docs/requirements.in +++ /dev/null @@ -1,2 +0,0 @@ -sphinx==6.2.1 -sphinx-rtd-theme diff --git a/docs/requirements.txt b/docs/requirements.txt deleted file mode 100644 index 89b855e..0000000 --- a/docs/requirements.txt +++ /dev/null @@ -1,57 +0,0 @@ -# -# This file is autogenerated by pip-compile with Python 3.10 -# by the following command: -# -# pip-compile requirements.in -# -alabaster==0.7.13 - # via sphinx -babel==2.12.1 - # via sphinx -certifi==2023.7.22 - # via requests -charset-normalizer==3.2.0 - # via requests -docutils==0.18.1 - # via - # sphinx - # sphinx-rtd-theme -idna==3.4 - # via requests -imagesize==1.4.1 - # via sphinx -jinja2==3.1.2 - # via sphinx -markupsafe==2.1.3 - # via jinja2 -packaging==23.1 - # via sphinx -pygments==2.15.1 - # via sphinx -requests==2.31.0 - # via sphinx -snowballstemmer==2.2.0 - # via sphinx -sphinx==6.2.1 - # via - # -r requirements.in - # sphinx-rtd-theme - # sphinxcontrib-jquery -sphinx-rtd-theme==1.2.2 - # via -r requirements.in -sphinxcontrib-applehelp==1.0.4 - # via sphinx -sphinxcontrib-devhelp==1.0.2 - # via sphinx -sphinxcontrib-htmlhelp==2.0.1 - # via sphinx -sphinxcontrib-jquery==4.1 - # via sphinx-rtd-theme -sphinxcontrib-jsmath==1.0.1 - # via sphinx -sphinxcontrib-qthelp==1.0.3 - # via sphinx -sphinxcontrib-serializinghtml==1.1.5 - # via sphinx -urllib3==2.0.4 - # via requests diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..b1a42fa --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,80 @@ +[build-system] +requires = ["hatchling", "hatch-vcs"] +build-backend = "hatchling.build" + +[project] +name = "fypp" +description = "Python powered Fortran preprocessor" +dynamic = ["version"] +authors = [ + { name = "Bálint Aradi", email = "aradi@uni-bremen.de" }, +] +requires-python = ">=3.5" +license = { text = "BSD-2-Clause" } +license-files = { paths = ["LICENSE.txt"] } +readme = "README.rst" + +classifiers = [ + 'Development Status :: 5 - Production/Stable', + 'Intended Audience :: Developers', + 'Intended Audience :: Science/Research', + 'Topic :: Software Development :: Code Generators', + 'Topic :: Software Development :: Pre-processors', + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + 'Programming Language :: Python :: 3.5', + '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", +] +keywords=[ + "fortran", + "metaprogramming", + "pre-processor", +] + +[project.urls] +homepage = "https://github.com/aradi/fypp" +documentation = "https://fypp.readthedocs.io/" +repository = "https://github.com/aradi/fypp" + +[project.scripts] +fypp = "fypp:run_fypp" + +[project.optional-dependencies] +test = [ + "pytest", +] +docs = [ + "sphinx", + "sphinx-rtd-theme", +] +dev = [ + "fypp[test]", +] + +[tool.hatch] +version.source = "vcs" +build.hooks.vcs.version-file = "src/fypp/_version.py" + +[tool.pytest.ini_options] +testpaths = ["test"] +python_files = "test_*.py" + +[tool.tox] +legacy_tox_ini = """ + [tox] + envlist = py34, py35, py36, py37, py38, py39 + + [testenv] + skip_missing_interpreters = + true + setenv = + PYTHONPATH = {toxinidir}/src + changedir=test + commands=python test_fypp.py +""" diff --git a/setup.py b/setup.py deleted file mode 100644 index ff587cd..0000000 --- a/setup.py +++ /dev/null @@ -1,54 +0,0 @@ -# -*- coding: utf-8 -*- -from setuptools import setup -from codecs import open -from os import path - -here = path.abspath(path.dirname(__file__)) - -with open(path.join(here, 'README.rst'), encoding='utf-8') as f: - long_description = f.read() - -setup( - name='fypp', - - version='3.2', - - description='Python powered Fortran preprocessor', - long_description=long_description, - - url='https://github.com/aradi/fypp', - - author='Bálint Aradi', - author_email='aradi@uni-bremen.de', - - license='BSD', - - classifiers=[ - 'Development Status :: 5 - Production/Stable', - - 'Intended Audience :: Developers', - 'Intended Audience :: Science/Research', - 'Topic :: Software Development :: Code Generators', - 'Topic :: Software Development :: Pre-processors', - - 'License :: OSI Approved :: BSD License', - - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - ], - - keywords='fortran metaprogramming pre-processor', - - package_dir={'': 'src'}, - py_modules=['fypp'], - - entry_points={ - 'console_scripts': [ - 'fypp=fypp:run_fypp', - ], - }, -) diff --git a/test/runtests.sh b/test/runtests.sh deleted file mode 100755 index 422e171..0000000 --- a/test/runtests.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash -testdir="$(dirname $0)" -if [ $# -gt 0 ]; then - pythons=$* -else - pythons="python3" -fi -root=".." -if [ -z "$PYTHONPATH" ]; then - export PYTHONPATH="$root/src" -else - export PYTHONPATH="$root/src:$PYTHONPATH" -fi -cd $testdir -failed="0" -failing_pythons="" -for python in $pythons; do - echo "Testing with interpreter '$python'" - $python test_fypp.py - exitcode=$? - if [ $exitcode != 0 ]; then - failed="$(($failed + 1))" - if [ -z "$failing_pythons" ]; then - failing_pythons=$python - else - failing_pythons="$failing_pythons, $python" - fi - fi -done -echo -if [ $failed -gt 0 ]; then - echo "Failing test runs: $failed" >&2 - echo "Failing interpreter(s): $failing_pythons" >&2 - exit 1 -else - echo "All test runs finished successfully" - exit 0 -fi diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 295aed6..0000000 --- a/tox.ini +++ /dev/null @@ -1,10 +0,0 @@ -[tox] -envlist = py34, py35, py36, py37, py38, py39 - -[testenv] -skip_missing_interpreters = - true -setenv = - PYTHONPATH = {toxinidir}/src -changedir=test -commands=python test_fypp.py From 59d416052ebe8e903c739bc32410550f8c560552 Mon Sep 17 00:00:00 2001 From: Cristian Le Date: Tue, 4 Jul 2023 09:01:31 +0200 Subject: [PATCH 3/6] dynamic version Signed-off-by: Cristian Le --- .git_archival.txt | 4 +++ .gitignore | 3 ++ utils/bump-version.py | 65 ------------------------------------------- 3 files changed, 7 insertions(+), 65 deletions(-) create mode 100644 .git_archival.txt delete mode 100755 utils/bump-version.py diff --git a/.git_archival.txt b/.git_archival.txt new file mode 100644 index 0000000..8fb235d --- /dev/null +++ b/.git_archival.txt @@ -0,0 +1,4 @@ +node: $Format:%H$ +node-date: $Format:%cI$ +describe-name: $Format:%(describe:tags=true,match=*[0-9]*)$ +ref-names: $Format:%D$ diff --git a/.gitignore b/.gitignore index 7294e66..d1272c3 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,6 @@ docs/_build .tox _gitmsg.saved.txt + +### Project specific +src/fypp/_version.py diff --git a/utils/bump-version.py b/utils/bump-version.py deleted file mode 100755 index de4e739..0000000 --- a/utils/bump-version.py +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/env python3 -import sys -import re -import os - -VERSION_PATTERN = r'\d+\.\d+(?:\.\d+)?(?:-\w+)?' -FILES_PATTERNS = [ ('bin/fypp', - r'^VERSION\s*=\s*([\'"]){}\1'.format(VERSION_PATTERN), - "VERSION = '{version}'"), - ('docs/fypp.rst', - r'Fypp Version[ ]*{}.'.format(VERSION_PATTERN), - 'Fypp Version {shortversion}.'), - ('setup.py', - r'version\s*=\s*([\'"]){}\1'.format(VERSION_PATTERN), - "version='{version}'"), - ('docs/conf.py', - r'version\s*=\s*([\'"]){}\1'.format(VERSION_PATTERN), - "version = '{shortversion}'"), - ('docs/conf.py', - r'release\s*=\s*([\'"]){}\1'.format(VERSION_PATTERN), - "release = '{version}'"), -] - -if len(sys.argv) < 2: - print("Missing version string") - sys.exit(1) - - -version = sys.argv[1] -shortversion = '.'.join(version.split('.')[0:2]) - -match = re.match(VERSION_PATTERN, version) -if match is None: - print("Invalid version string") - sys.exit(1) - -rootdir = os.path.join(os.path.dirname(sys.argv[0]), '..') -for fname, regexp, repl in FILES_PATTERNS: - fname = os.path.join(rootdir, fname) - print("Replacments in '{}': ".format(fname), end='') - fp = open(fname, 'r') - txt = fp.read() - fp.close() - replacement = repl.format(version=version, shortversion=shortversion) - newtxt, nsub = re.subn(regexp, replacement, txt, flags=re.MULTILINE) - print(nsub) - fp = open(fname, 'w') - fp.write(newtxt) - fp.close() - - -# Replace version number in Change Log and adapt decoration below -fname = os.path.join(rootdir, 'CHANGELOG.rst') -print("Replacments in '{}': ".format(fname), end='') -fp = open(fname, 'r') -txt = fp.read() -fp.close() -decoration = '=' * len(version) -newtxt, nsub = re.subn( - '^Unreleased\s*\n=+', version + '\n' + decoration, txt, - count=1, flags=re.MULTILINE) -print(nsub) -fp = open(fname, 'w') -fp.write(newtxt) -fp.close() From e4cd7620394dd9c65b31a1565a099ac33f0d6c22 Mon Sep 17 00:00:00 2001 From: Cristian Le Date: Tue, 4 Jul 2023 09:02:06 +0200 Subject: [PATCH 4/6] Reorganize fypp package/module Signed-off-by: Cristian Le --- docs/fypp.rst | 4 +- pyproject.toml | 2 +- src/fypp.py | 1 - src/fypp/__init__.py | 6 ++ src/fypp/__main__.py | 3 + src/fypp/cli.py | 148 +++++++++++++++++++++++++++++++++++ bin/fypp => src/fypp/fypp.py | 147 ++-------------------------------- test/test_fypp.py | 15 ++-- 8 files changed, 175 insertions(+), 151 deletions(-) delete mode 120000 src/fypp.py create mode 100644 src/fypp/__init__.py create mode 100644 src/fypp/__main__.py create mode 100644 src/fypp/cli.py rename bin/fypp => src/fypp/fypp.py (94%) diff --git a/docs/fypp.rst b/docs/fypp.rst index a35576b..8b57100 100644 --- a/docs/fypp.rst +++ b/docs/fypp.rst @@ -2145,10 +2145,10 @@ Fypp :members: -FyppOptions +FyppDefaults =========== -.. autoclass:: FyppOptions +.. autoclass:: FyppDefaults :members: diff --git a/pyproject.toml b/pyproject.toml index b1a42fa..df84a92 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,7 +43,7 @@ documentation = "https://fypp.readthedocs.io/" repository = "https://github.com/aradi/fypp" [project.scripts] -fypp = "fypp:run_fypp" +fypp = "fypp.cli:run_fypp" [project.optional-dependencies] test = [ diff --git a/src/fypp.py b/src/fypp.py deleted file mode 120000 index fea8343..0000000 --- a/src/fypp.py +++ /dev/null @@ -1 +0,0 @@ -../bin/fypp \ No newline at end of file diff --git a/src/fypp/__init__.py b/src/fypp/__init__.py new file mode 100644 index 0000000..308f088 --- /dev/null +++ b/src/fypp/__init__.py @@ -0,0 +1,6 @@ +from __future__ import annotations + +from ._version import version as __version__ +from .fypp import Fypp + +__all__ = ["__version__", "Fypp"] diff --git a/src/fypp/__main__.py b/src/fypp/__main__.py new file mode 100644 index 0000000..de33fa1 --- /dev/null +++ b/src/fypp/__main__.py @@ -0,0 +1,3 @@ +from .cli import run_fypp + +run_fypp() diff --git a/src/fypp/cli.py b/src/fypp/cli.py new file mode 100644 index 0000000..96e1038 --- /dev/null +++ b/src/fypp/cli.py @@ -0,0 +1,148 @@ +import argparse +import sys +from .fypp import FyppDefaults, Fypp, FyppStopRequest, FyppFatalError, _formatted_exception, USER_ERROR_EXIT_CODE, \ + ERROR_EXIT_CODE +from ._version import __version__ + + +def get_option_parser() -> argparse.ArgumentParser: + """ + Returns an option parser for the Fypp command line tool. + + :return: Parser which can create an optparse.Values object with + Fypp settings based on command line arguments. + """ + defs = FyppDefaults() + parser = argparse.ArgumentParser( + prog="fypp", + description=""" + Preprocesses source code with Fypp directives. The input is + read from INFILE (default: \'-\', stdin) and written to + OUTFILE (default: \'-\', stdout). + """) + parser.add_argument('--version', action='version', version=__version__) + + parser.add_argument('-D', '--define', action='append', dest='defines', + metavar='VAR[=VALUE]', default=defs.defines, + help=""" + define variable, value is interpreted as + Python expression (e.g \'-DDEBUG=1\' sets DEBUG to the + integer 1) or set to None if omitted + """) + + parser.add_argument('-I', '--include', action='append', dest='includes', + metavar='INCDIR', default=defs.includes, + help=""" + add directory to the search paths for include files + """) + parser.add_argument('-m', '--module', action='append', dest='modules', + metavar='MOD', default=defs.modules, + help=""" + import a python module at startup (import only trustworthy modules + as they have access to an **unrestricted** Python environment!) + """) + + parser.add_argument('-M', '--module-dir', action='append', + dest='moduledirs', metavar='MODDIR', + default=defs.moduledirs, + help=""" + directory to be searched for user imported modules before + looking up standard locations in sys.path + """) + + parser.add_argument('-n', '--line-numbering', action='store_true', + dest='line_numbering', default=defs.line_numbering, + help=""" + emit line numbering markers + """) + parser.add_argument('-N', '--line-numbering-mode', metavar='MODE', + choices=['full', 'nocontlines'], + default=defs.line_numbering_mode, + dest='line_numbering_mode', + help=""" + line numbering mode, 'full' (default): line numbering + markers generated whenever source and output lines are out + of sync, 'nocontlines': line numbering markers omitted + for continuation lines + """) + parser.add_argument('--line-marker-format', metavar='FMT', + choices=['cpp', 'gfortran5', 'std'], + dest='line_marker_format', + default=defs.line_marker_format, + help=""" + line numbering marker format, currently 'std', 'cpp' and + 'gfortran5' are supported, where 'std' emits #line pragmas + similar to standard tools, 'cpp' produces line directives as + emitted by GNU cpp, and 'gfortran5' cpp line directives with a + workaround for a bug introduced in GFortran 5. Default: 'cpp'. + """) + parser.add_argument('-l', '--line-length', type=int, metavar='LEN', + dest='line_length', default=defs.line_length, + help=""" + maximal line length (default: 132), lines modified by the + preprocessor are folded if becoming longer + """) + parser.add_argument('-f', '--folding-mode', metavar='MODE', + choices=['smart', 'simple', 'brute'], dest='folding_mode', + default=defs.folding_mode, + help=""" + line folding mode, 'smart' (default): indentation context + and whitespace aware, 'simple': indentation context aware, + 'brute': mechnical folding + """) + + parser.add_argument('-F', '--no-folding', action='store_true', + dest='no_folding', default=defs.no_folding, + help=""" + suppress line folding + """) + parser.add_argument('--indentation', type=int, metavar='IND', + dest='indentation', default=defs.indentation, + help=""" + indentation to use for continuation lines (default 4) + """) + parser.add_argument('--fixed-format', action='store_true', + dest='fixed_format', default=defs.fixed_format, + help=""" + produce fixed format output (any settings for options + --line-length, --folding-method and --indentation are ignored) + """) + parser.add_argument('--encoding', metavar='ENC', default=defs.encoding, + help=""" + character encoding for reading/writing files. Default: 'utf-8'. + Note: reading from stdin and writing to stdout is encoded + according to the current locale and is not affected by this setting. + """) + parser.add_argument('-p', '--create-parents', action='store_true', + dest='create_parent_folder', + default=defs.create_parent_folder, + help=""" + create parent folders of the output file if they do not exist + """) + parser.add_argument('--file-var-root', metavar='DIR', + dest='file_var_root', + default=defs.file_var_root, + help=""" + in variables _FILE_ and _THIS_FILE_, use relative paths with DIR + as root directory. Note: the input file and all included files + must be in DIR or in a directory below. + """) + + return parser + + +def run_fypp(): + """Run the Fypp command line tool.""" + parser = get_option_parser() + opts, leftover = parser.parse_known_args() + infile = leftover[0] if len(leftover) > 0 else '-' + outfile = leftover[1] if len(leftover) > 1 else '-' + try: + tool = Fypp(opts) + tool.process_file(infile, outfile) + except FyppStopRequest as exc: + sys.stderr.write(_formatted_exception(exc)) + sys.exit(USER_ERROR_EXIT_CODE) + except FyppFatalError as exc: + sys.stderr.write(_formatted_exception(exc)) + sys.exit(ERROR_EXIT_CODE) diff --git a/bin/fypp b/src/fypp/fypp.py similarity index 94% rename from bin/fypp rename to src/fypp/fypp.py index 4207219..3a96798 100755 --- a/bin/fypp +++ b/src/fypp/fypp.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- ################################################################################ # # fypp -- Python powered Fortran preprocessor @@ -37,7 +35,7 @@ * `Fypp`_: The actual Fypp preprocessor. It returns for a given input the preprocessed output. -* `FyppOptions`_: Contains customizable settings controlling the behaviour of +* `FyppDefaults`_: Contains customizable settings controlling the behaviour of `Fypp`_. Alternatively, the function `get_option_parser()`_ can be used to obtain an option parser, which can create settings based on command line arguments. @@ -66,8 +64,6 @@ # Prevent cluttering user directory with Python bytecode sys.dont_write_bytecode = True -VERSION = '3.2' - STDIN = '' FILEOBJ = '' @@ -2435,10 +2431,10 @@ class Fypp: tool = fypp.Fypp() output = tool.process_text('#:if DEBUG > 0\\nprint *, "DEBUG"\\n#:endif\\n') - If you want to fine tune Fypps behaviour, pass a customized `FyppOptions`_ + If you want to fine tune Fypps behaviour, pass a customized `FyppDefaults`_ instance at initialization:: - options = fypp.FyppOptions() + options = fypp.FyppDefaults() options.fixed_format = True tool = fypp.Fypp(options) @@ -2476,9 +2472,9 @@ def __init__(self): Args: options (object): Object containing the settings for Fypp. You typically - would pass a customized `FyppOptions`_ instance or an + would pass a customized `FyppDefaults`_ instance or an ``optparse.Values`` object as returned by the option parser. If not - present, the default settings in `FyppOptions`_ are used. + present, the default settings in `FyppDefaults`_ are used. evaluator_factory (function): Factory function that returns an Evaluator object. Its call signature must match that of the Evaluator constructor. If not present, ``Evaluator`` is used. @@ -2499,7 +2495,7 @@ def __init__(self, options=None, evaluator_factory=Evaluator, syspath = self._get_syspath_without_scriptdir() self._adjust_syspath(syspath) if options is None: - options = FyppOptions() + options = FyppDefaults() if inspect.signature(evaluator_factory) == inspect.signature(Evaluator): evaluator = evaluator_factory() else: @@ -2630,7 +2626,7 @@ def _adjust_syspath(syspath): sys.path = syspath -class FyppOptions(optparse.Values): +class FyppDefaults: '''Container for Fypp options with default values. @@ -2807,131 +2803,6 @@ def __call__(self, line): return [line] -def get_option_parser(): - '''Returns an option parser for the Fypp command line tool. - - Returns: - OptionParser: Parser which can create an optparse.Values object with - Fypp settings based on command line arguments. - ''' - defs = FyppOptions() - fypp_name = 'fypp' - fypp_desc = 'Preprocesses source code with Fypp directives. The input is '\ - 'read from INFILE (default: \'-\', stdin) and written to '\ - 'OUTFILE (default: \'-\', stdout).' - fypp_version = fypp_name + ' ' + VERSION - usage = '%prog [options] [INFILE] [OUTFILE]' - parser = optparse.OptionParser(prog=fypp_name, description=fypp_desc, - version=fypp_version, usage=usage) - - msg = 'define variable, value is interpreted as ' \ - 'Python expression (e.g \'-DDEBUG=1\' sets DEBUG to the ' \ - 'integer 1) or set to None if omitted' - parser.add_option('-D', '--define', action='append', dest='defines', - metavar='VAR[=VALUE]', default=defs.defines, help=msg) - - msg = 'add directory to the search paths for include files' - parser.add_option('-I', '--include', action='append', dest='includes', - metavar='INCDIR', default=defs.includes, help=msg) - - msg = 'import a python module at startup (import only trustworthy modules '\ - 'as they have access to an **unrestricted** Python environment!)' - parser.add_option('-m', '--module', action='append', dest='modules', - metavar='MOD', default=defs.modules, help=msg) - - msg = 'directory to be searched for user imported modules before '\ - 'looking up standard locations in sys.path' - parser.add_option('-M', '--module-dir', action='append', - dest='moduledirs', metavar='MODDIR', - default=defs.moduledirs, help=msg) - - msg = 'emit line numbering markers' - parser.add_option('-n', '--line-numbering', action='store_true', - dest='line_numbering', default=defs.line_numbering, - help=msg) - - msg = 'line numbering mode, \'full\' (default): line numbering '\ - 'markers generated whenever source and output lines are out '\ - 'of sync, \'nocontlines\': line numbering markers omitted '\ - 'for continuation lines' - parser.add_option('-N', '--line-numbering-mode', metavar='MODE', - choices=['full', 'nocontlines'], - default=defs.line_numbering_mode, - dest='line_numbering_mode', help=msg) - - msg = 'line numbering marker format, currently \'std\', \'cpp\' and '\ - '\'gfortran5\' are supported, where \'std\' emits #line pragmas '\ - 'similar to standard tools, \'cpp\' produces line directives as '\ - 'emitted by GNU cpp, and \'gfortran5\' cpp line directives with a '\ - 'workaround for a bug introduced in GFortran 5. Default: \'cpp\'.' - parser.add_option('--line-marker-format', metavar='FMT', - choices=['cpp', 'gfortran5', 'std'], - dest='line_marker_format', - default=defs.line_marker_format, help=msg) - - msg = 'maximal line length (default: 132), lines modified by the '\ - 'preprocessor are folded if becoming longer' - parser.add_option('-l', '--line-length', type=int, metavar='LEN', - dest='line_length', default=defs.line_length, help=msg) - - msg = 'line folding mode, \'smart\' (default): indentation context '\ - 'and whitespace aware, \'simple\': indentation context aware, '\ - '\'brute\': mechnical folding' - parser.add_option('-f', '--folding-mode', metavar='MODE', - choices=['smart', 'simple', 'brute'], dest='folding_mode', - default=defs.folding_mode, help=msg) - - msg = 'suppress line folding' - parser.add_option('-F', '--no-folding', action='store_true', - dest='no_folding', default=defs.no_folding, help=msg) - - msg = 'indentation to use for continuation lines (default 4)' - parser.add_option('--indentation', type=int, metavar='IND', - dest='indentation', default=defs.indentation, help=msg) - - msg = 'produce fixed format output (any settings for options '\ - '--line-length, --folding-method and --indentation are ignored)' - parser.add_option('--fixed-format', action='store_true', - dest='fixed_format', default=defs.fixed_format, help=msg) - - msg = 'character encoding for reading/writing files. Default: \'utf-8\'. '\ - 'Note: reading from stdin and writing to stdout is encoded '\ - 'according to the current locale and is not affected by this setting.' - parser.add_option('--encoding', metavar='ENC', default=defs.encoding, - help=msg) - - msg = 'create parent folders of the output file if they do not exist' - parser.add_option('-p', '--create-parents', action='store_true', - dest='create_parent_folder', - default=defs.create_parent_folder, help=msg) - - msg = 'in variables _FILE_ and _THIS_FILE_, use relative paths with DIR '\ - 'as root directory. Note: the input file and all included files '\ - 'must be in DIR or in a directory below.' - parser.add_option('--file-var-root', metavar='DIR', dest='file_var_root', - default=defs.file_var_root, help=msg) - - return parser - - -def run_fypp(): - '''Run the Fypp command line tool.''' - options = FyppOptions() - optparser = get_option_parser() - opts, leftover = optparser.parse_args(values=options) - infile = leftover[0] if len(leftover) > 0 else '-' - outfile = leftover[1] if len(leftover) > 1 else '-' - try: - tool = Fypp(opts) - tool.process_file(infile, outfile) - except FyppStopRequest as exc: - sys.stderr.write(_formatted_exception(exc)) - sys.exit(USER_ERROR_EXIT_CODE) - except FyppFatalError as exc: - sys.stderr.write(_formatted_exception(exc)) - sys.exit(ERROR_EXIT_CODE) - - def linenumdir_cpp(linenr, fname, flag=None): """Returns a GNU cpp style line directive. @@ -3082,7 +2953,3 @@ def _formatted_exception(exc): out.append('\n' + _formatted_exception(exc.__cause__)) out.append('\n') return ''.join(out) - - -if __name__ == '__main__': - run_fypp() diff --git a/test/test_fypp.py b/test/test_fypp.py index 1dcc3ac..474b963 100644 --- a/test/test_fypp.py +++ b/test/test_fypp.py @@ -2,7 +2,8 @@ from pathlib import Path import platform import unittest -import fypp +import fypp.fypp as fypp +from fypp.cli import get_option_parser def _linenum(linenr, fname=None, flag=None): @@ -2957,8 +2958,8 @@ def _get_test_output_method(args, inp, out): def test_output(self): '''Tests whether Fypp result matches expected output.''' - optparser = fypp.get_option_parser() - options, leftover = optparser.parse_args(args) + parser = get_option_parser() + options, leftover = parser.parse_known_args(args) self.assertEqual(len(leftover), 0) tool = fypp.Fypp(options) result = tool.process_text(inp) @@ -2980,8 +2981,8 @@ def _get_test_output_from_file_input_method(args, inputfile, out): def test_output_from_file_input(self): '''Tests whether Fypp result matches expected output when input is in a file.''' - optparser = fypp.get_option_parser() - options, leftover = optparser.parse_args(args) + parser = get_option_parser() + options, leftover = parser.parse_known_args(args) self.assertEqual(len(leftover), 0) tool = fypp.Fypp(options) result = tool.process_file(inputfile) @@ -3006,8 +3007,8 @@ def _get_test_exception_method(args, inp, exceptions): def test_exception(self): '''Tests whether Fypp throws the correct exception.''' - optparser = fypp.get_option_parser() - options, leftover = optparser.parse_args(args) + parser = get_option_parser() + options, leftover = parser.parse_known_args(args) self.assertEqual(len(leftover), 0) try: tool = fypp.Fypp(options) From 60e181e5a19fc90113c8c0024940fee8fc4919b8 Mon Sep 17 00:00:00 2001 From: Cristian Le Date: Fri, 8 Sep 2023 11:37:24 +0200 Subject: [PATCH 5/6] Basic Github Action Signed-off-by: Cristian Le --- .codecov.yaml | 3 ++ .github/dependabot.yml | 7 ++++ .github/workflows/ci.yaml | 40 +++++++++++++++++++ .github/workflows/release.yaml | 52 +++++++++++++++++++++++++ .github/workflows/step_build-wheel.yaml | 29 ++++++++++++++ .github/workflows/step_test.yaml | 45 +++++++++++++++++++++ pyproject.toml | 4 ++ 7 files changed, 180 insertions(+) create mode 100644 .codecov.yaml create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/ci.yaml create mode 100644 .github/workflows/release.yaml create mode 100644 .github/workflows/step_build-wheel.yaml create mode 100644 .github/workflows/step_test.yaml diff --git a/.codecov.yaml b/.codecov.yaml new file mode 100644 index 0000000..6a4917b --- /dev/null +++ b/.codecov.yaml @@ -0,0 +1,3 @@ +codecov: + notify: + after_n_builds: 5 diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..491deae --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 +updates: +- package-ecosystem: pip + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10 diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..77681a6 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,40 @@ +name: CI + +on: + workflow_dispatch: + inputs: + upload-wheel: + type: boolean + required: false + default: false + description: Upload wheel as an artifact + pull_request: + branches: [ main ] + push: + branches: [ main ] + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + tests: + uses: ./.github/workflows/step_test.yaml + + build-wheel: + uses: ./.github/workflows/step_build-wheel.yaml + needs: [ tests ] + with: + upload: ${{ inputs.upload-wheel || false }} + pass: + needs: [tests, build-wheel] + runs-on: ubuntu-latest + steps: + - name: Check all CI action + uses: re-actors/alls-green@release/v1 + with: + jobs: ${{ toJSON(needs) }} + if: always() diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..343df84 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,52 @@ +name: Prepare release + +on: + push: + tags: + - "v[0-9]+.[0-9]+.[0-9]+" + - "v[0-9]+.[0-9]+.[0-9]+-rc[0-9]+" + workflow_dispatch: + inputs: + ref: + description: Tag to release + required: true + type: string + +permissions: + contents: read + +jobs: + tests: + uses: ./.github/workflows/step_test.yaml + build-wheel: + needs: [ tests ] + uses: ./.github/workflows/step_build-wheel.yaml + with: + ref: ${{ inputs.ref }} + upload_pypi: + name: Upload to PyPI repository + needs: [ tests, build-wheel ] + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/project/click-option-group/ + permissions: + id-token: write + steps: + - uses: actions/download-artifact@v3 + with: + name: artifact + path: dist + - name: Publish package to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + release: + needs: [ upload_pypi ] + name: Create release + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: softprops/action-gh-release@v1 + with: + name: click-option-group ${{ github.ref_name }} + prerelease: ${{ contains(github.ref, 'rc') }} + generate_release_notes: true diff --git a/.github/workflows/step_build-wheel.yaml b/.github/workflows/step_build-wheel.yaml new file mode 100644 index 0000000..b1cb720 --- /dev/null +++ b/.github/workflows/step_build-wheel.yaml @@ -0,0 +1,29 @@ +on: + workflow_call: + inputs: + upload: + description: Upload wheel as artifact + required: false + type: boolean + default: true + ref: + description: Tag to release + required: false + type: string + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ inputs.ref }} + - name: Build package + run: pipx run build + - uses: actions/upload-artifact@v3 + with: + path: dist/* + if: ${{ inputs.upload }} diff --git a/.github/workflows/step_test.yaml b/.github/workflows/step_test.yaml new file mode 100644 index 0000000..a9cd07d --- /dev/null +++ b/.github/workflows/step_test.yaml @@ -0,0 +1,45 @@ +on: + workflow_call: + +permissions: + contents: read + +jobs: + tests: + name: Check with Python ${{ matrix.python-version }} ${{ matrix.experimental && '(Experimental)' }} + needs: [ pre-commit ] + continue-on-error: ${{ matrix.experimental || false }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: [ "3.7", "3.8", "3.9", "3.10", "3.11", "3.12" ] + include: + - python-version: "3.12" + experimental: true + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + allow-prereleases: true + + - name: Install package + run: pip install -e .[test-cov] + - name: Test package + run: pytest --cov --cov-report=xml + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + with: + name: python ${{ matrix.python-version }} + flags: python-${{ matrix.python-version }} + + pass: + needs: [ tests ] + runs-on: ubuntu-latest + steps: + - name: Check test jobs + uses: re-actors/alls-green@release/v1 + with: + jobs: ${{ toJSON(needs) }} + if: always() diff --git a/pyproject.toml b/pyproject.toml index df84a92..3199392 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,6 +49,10 @@ fypp = "fypp.cli:run_fypp" test = [ "pytest", ] +test-cov = [ + "fypp[test]", + "pytest-cov", +] docs = [ "sphinx", "sphinx-rtd-theme", From cc34b3786efd6b7942c0c0b2feb562c3fa347db3 Mon Sep 17 00:00:00 2001 From: Cristian Le Date: Tue, 4 Jul 2023 09:00:56 +0200 Subject: [PATCH 6/6] Fix pytest Signed-off-by: Cristian Le --- test/test_fypp.py | 47 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/test/test_fypp.py b/test/test_fypp.py index 474b963..13eee71 100644 --- a/test/test_fypp.py +++ b/test/test_fypp.py @@ -1,10 +1,33 @@ '''Unit tests for testing Fypp.''' +from contextlib import contextmanager +from os import getcwd, chdir from pathlib import Path import platform import unittest import fypp.fypp as fypp from fypp.cli import get_option_parser +DIR = Path(__file__).parent.resolve() +BASE = DIR.parent + + +@contextmanager +def cd(target): + """ + Manage cd in a pushd/popd fashion. + + Usage: + + with cd(tmpdir): + do something in tmpdir + """ + curdir = getcwd() + chdir(target) + try: + yield + finally: + chdir(curdir) + def _linenum(linenr, fname=None, flag=None): if fname is None: @@ -1413,7 +1436,7 @@ def _importmodule(module): ), ('escape_comment', ([], - 'A\n #\! Comment\n', + 'A\n #\\! Comment\n', 'A\n #! Comment\n', ) ), @@ -2151,8 +2174,8 @@ def _importmodule(module): ) ), ('file_var_root_abs', - ([f"--file-var-root={Path.cwd()}"], - f"{Path.cwd() / 'input/filevarroot.fypp'}", + ([f"--file-var-root={DIR}"], + f"{DIR / 'input/filevarroot.fypp'}", 'FILE: input/filevarroot.fypp:1\n' 'THIS_FILE: input/filevarroot.fypp:2\n' '---\n' @@ -2961,8 +2984,10 @@ def test_output(self): parser = get_option_parser() options, leftover = parser.parse_known_args(args) self.assertEqual(len(leftover), 0) - tool = fypp.Fypp(options) - result = tool.process_text(inp) + # TODO: Use proper test fixture to temporary folder + with cd(DIR): + tool = fypp.Fypp(options) + result = tool.process_text(inp) self.assertEqual(out, result) return test_output @@ -2984,8 +3009,10 @@ def test_output_from_file_input(self): parser = get_option_parser() options, leftover = parser.parse_known_args(args) self.assertEqual(len(leftover), 0) - tool = fypp.Fypp(options) - result = tool.process_file(inputfile) + # TODO: Use proper test fixture to temporary folder + with cd(DIR): + tool = fypp.Fypp(options) + result = tool.process_file(inputfile) self.assertEqual(out, result) return test_output_from_file_input @@ -3011,8 +3038,10 @@ def test_exception(self): options, leftover = parser.parse_known_args(args) self.assertEqual(len(leftover), 0) try: - tool = fypp.Fypp(options) - _ = tool.process_text(inp) + # TODO: Use proper test fixture to temporary folder + with cd(DIR): + tool = fypp.Fypp(options) + _ = tool.process_text(inp) except Exception as e: raised = e else: