diff --git a/.github/actions/install_zcbor/action.yaml b/.github/actions/install_zcbor/action.yaml index fcb115ce..83edb454 100644 --- a/.github/actions/install_zcbor/action.yaml +++ b/.github/actions/install_zcbor/action.yaml @@ -11,14 +11,14 @@ runs: shell: sh run: | pip3 install -U pip - pip3 install -U setuptools + pip3 install -U build pip3 install -U -r scripts/requirements.txt - name: Install zcbor package if: ${{ inputs.zcbor_package == 'bdist_wheel' }} shell: sh run: | - python3 setup.py sdist bdist_wheel + python3 -m build pip3 install dist/zcbor-*.tar.gz pip3 uninstall -y zcbor pip3 install dist/zcbor-*.whl @@ -27,10 +27,10 @@ runs: if: ${{ inputs.zcbor_package == 'setup_install' }} shell: sh run: | - python3 setup.py install + pip3 install . - name: Install zcbor package if: ${{ inputs.zcbor_package == 'setup_develop' }} shell: sh run: | - python3 setup.py develop + pip3 install -e . diff --git a/.github/workflows/run-tests.yaml b/.github/workflows/run-tests.yaml index 041352bc..e1a115f5 100644 --- a/.github/workflows/run-tests.yaml +++ b/.github/workflows/run-tests.yaml @@ -64,13 +64,12 @@ jobs: - name: Install west and dependencies run: | pip install -U pip - pip install -U setuptools - pip install -U wheel + pip install -U build pip install -U -r scripts/requirements.txt - name: Generate and install zcbor package run: | - python setup.py bdist_wheel + python build bdist_wheel pip install dist/zcbor-0.7.99-py3-none-any.whl pip uninstall -y zcbor pip install -e . diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 1023f376..00000000 --- a/MANIFEST.in +++ /dev/null @@ -1 +0,0 @@ -include LICENSE README.md scripts/requirements-base.txt zcbor/VERSION diff --git a/README.md b/README.md index cec61a89..c87ed2dc 100644 --- a/README.md +++ b/README.md @@ -443,8 +443,8 @@ options: -c CDDL, --cddl CDDL Path to one or more input CDDL file(s). Passing multiple files is equivalent to concatenating them. --no-prelude Exclude the standard CDDL prelude from the build. The - prelude can be viewed at zcbor/cddl/prelude.cddl in - the repo, or together with the script. + prelude can be viewed at zcbor/prelude.cddl in the + repo, or together with the script. -v, --verbose Print more information while parsing CDDL and generating code. --default-max-qty DEFAULT_MAX_QTY, --dq DEFAULT_MAX_QTY @@ -540,8 +540,8 @@ options: -c CDDL, --cddl CDDL Path to one or more input CDDL file(s). Passing multiple files is equivalent to concatenating them. --no-prelude Exclude the standard CDDL prelude from the build. The - prelude can be viewed at zcbor/cddl/prelude.cddl in - the repo, or together with the script. + prelude can be viewed at zcbor/prelude.cddl in the + repo, or together with the script. -v, --verbose Print more information while parsing CDDL and generating code. -i INPUT, --input INPUT @@ -593,8 +593,8 @@ options: -c CDDL, --cddl CDDL Path to one or more input CDDL file(s). Passing multiple files is equivalent to concatenating them. --no-prelude Exclude the standard CDDL prelude from the build. The - prelude can be viewed at zcbor/cddl/prelude.cddl in - the repo, or together with the script. + prelude can be viewed at zcbor/prelude.cddl in the + repo, or together with the script. -v, --verbose Print more information while parsing CDDL and generating code. -i INPUT, --input INPUT diff --git a/__init__.py b/__init__.py new file mode 100644 index 00000000..d0db1949 --- /dev/null +++ b/__init__.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2021 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# + +from pathlib import Path + +from .zcbor.zcbor import ( + CddlValidationError, + DataTranslator, + main +) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..2b758678 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,40 @@ +[build-system] +requires = ['setuptools'] +build-backend = 'setuptools.build_meta' + +[project] +name = 'zcbor' +description = 'Code generation and validation using CDDL schemas' +readme = 'README.md' +license = {text = 'Apache'} +requires-python = ">=3.8" +classifiers = [ + 'Development Status :: 4 - Beta', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: Apache Software License', + 'Programming Language :: Python :: 3', + 'Programming Language :: C', + 'Topic :: File Formats :: JSON :: JSON Schema', + 'Topic :: Software Development :: Build Tools', + 'Topic :: Software Development :: Code Generators', +] +authors = [{name = 'Nordic Semiconductor ASA'}] +maintainers = [{name = 'Øyvind Rønningstad', email = 'oyvind.ronningstad@nordicsemi.no'}] +dynamic = ['dependencies', 'version'] + +[project.urls] +Homepage = 'https://github.com/NordicSemiconductor/zcbor' + +[project.scripts] +zcbor = 'zcbor:main' + +[tool.setuptools] +packages = ['zcbor', 'zcbor.src', 'zcbor.include', 'zcbor.zcbor'] +package-dir = {zcbor = '.'} + +[tool.setuptools.package-data] +zcbor = ['src/*', 'include/*', 'zcbor/*'] + +[tool.setuptools.dynamic] +version = {file = 'zcbor/VERSION'} +dependencies = {file = ['scripts/requirements-base.txt']} \ No newline at end of file diff --git a/add_helptext.py b/scripts/add_helptext.py similarity index 96% rename from add_helptext.py rename to scripts/add_helptext.py index 4cf3c9d4..2a844fd8 100644 --- a/add_helptext.py +++ b/scripts/add_helptext.py @@ -10,7 +10,7 @@ from pathlib import Path from sys import argv -p_root = Path(__file__).absolute().parents[0] +p_root = Path(__file__).absolute().parents[1] p_README = Path(p_root, 'README.md') pattern = r""" diff --git a/setup.py b/setup.py deleted file mode 100644 index cfbb61a8..00000000 --- a/setup.py +++ /dev/null @@ -1,91 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (c) 2021 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: Apache-2.0 -# - -import setuptools -from pathlib import Path -from re import sub - - -P_REPO_ROOT = Path(__file__).absolute().parents[0] -ZCBOR_URL = 'https://github.com/NordicSemiconductor/zcbor' - - -def absolute_links(text): - def new_link(match): - match_text = match.group(0) - path = Path(P_REPO_ROOT, match_text) - try: - path_exists = path.exists() - except OSError: - path_exists = False - if path_exists: - path_comp = "tree" if path.is_dir() else "blob" - url_prefix = f"{ZCBOR_URL}/{path_comp}/{get_version()}/" - return url_prefix + match_text - else: - return match_text - new_text = sub(r"(?<=\]\().*?(?=\))", new_link, text) - return new_text - - -def get_description(title_only=False): - """Use the README as the long description that appears on PyPI.""" - p_readme = Path(P_REPO_ROOT, 'README.md').absolute() - - if p_readme.is_file(): - with p_readme.open(encoding='utf-8') as f: - return f.readline().strip() if title_only else absolute_links(f.read()) - return '' - - -def get_dependencies(): - """Extract base requirement packages from file.""" - p_base_reqs = Path( - P_REPO_ROOT, 'scripts', 'requirements-base.txt').absolute() - - l_dependencies = list() - if p_base_reqs.is_file(): - with p_base_reqs.open(encoding='utf-8') as f: - for line in f.readlines(): - l_dependencies.append(line.strip()) - return l_dependencies - - -def get_version(): - p_version = Path(P_REPO_ROOT, 'zcbor', 'VERSION').absolute() - return p_version.read_text(encoding='utf-8').strip() - - -setuptools.setup( - name='zcbor', - version=get_version(), - description=get_description(title_only=True), - long_description=get_description(), - long_description_content_type='text/markdown', - url=ZCBOR_URL, - author='Nordic Semiconductor ASA', - license='Apache Software License', - classifiers=[ - "Development Status :: 4 - Beta", - "License :: OSI Approved :: Apache Software License", - "Programming Language :: Python :: 3", - "Topic :: Software Development :: Build Tools", - ], - python_requires='>=3.7', - packages=setuptools.find_packages(), - install_requires=get_dependencies(), - entry_points={ - 'console_scripts': ['zcbor=zcbor.zcbor:main'], - }, - include_package_data=True, - package_data={'': ['VERSION', 'cddl/prelude.cddl']}, - data_files=[ - ("lib/zcbor/include", - ("include/zcbor_decode.h", "include/zcbor_encode.h", "include/zcbor_common.h", - "include/zcbor_tags.h", "include/zcbor_debug.h")), - ("lib/zcbor/src", ("src/zcbor_decode.c", "src/zcbor_encode.c", "src/zcbor_common.c"))], -) diff --git a/tests/scripts/test_repo_files.py b/tests/scripts/test_repo_files.py index f985ee4d..e7f89d79 100644 --- a/tests/scripts/test_repo_files.py +++ b/tests/scripts/test_repo_files.py @@ -25,10 +25,9 @@ p_readme = p_root / "README.md" p_architecture = p_root / "ARCHITECTURE.md" p_release_notes = p_root / "RELEASE_NOTES.md" -p_init_py = p_root / 'zcbor' / '__init__.py' +p_init_py = p_root / '__init__.py' p_zcbor_py = p_root / 'zcbor' / 'zcbor.py' -p_setup_py = p_root / 'setup.py' -p_add_helptext = p_root / 'add_helptext.py' +p_add_helptext = p_root / 'scripts' / 'add_helptext.py' p_test_zcbor_py = p_tests / 'scripts' / 'test_zcbor.py' p_test_versions_py = p_tests / 'scripts' / 'test_versions.py' p_test_repo_files_py = p_tests / 'scripts' / 'test_repo_files.py' @@ -51,7 +50,7 @@ def do_codestyle(self, files, **kwargs): def test_codestyle(self): """Run codestyle tests on all Python scripts in the repo.""" - self.do_codestyle([p_init_py, p_setup_py, p_test_versions_py, p_test_repo_files_py]) + self.do_codestyle([p_init_py, p_test_versions_py, p_test_repo_files_py, p_add_helptext]) self.do_codestyle([p_zcbor_py], ignore=['W191', 'E101', 'W503']) self.do_codestyle([p_test_zcbor_py], ignore=['E402', 'E501', 'W503']) diff --git a/tests/scripts/test_zcbor.py b/tests/scripts/test_zcbor.py index 3c13eb4a..ad454f73 100644 --- a/tests/scripts/test_zcbor.py +++ b/tests/scripts/test_zcbor.py @@ -22,7 +22,7 @@ except ImportError: print(""" The zcbor package must be installed to run these tests. -During development, install with `python3 setup.py develop` to install in a way +During development, install with `pip3 install -e .` to install in a way that picks up changes in the files without having to reinstall. """) exit(1) @@ -48,7 +48,7 @@ p_yaml_compat_cddl = Path(p_tests, 'cases', 'yaml_compatibility.cddl') p_yaml_compat_yaml = Path(p_tests, 'cases', 'yaml_compatibility.yaml') p_README = Path(p_root, 'README.md') -p_prelude = Path(p_root, 'zcbor', 'cddl', 'prelude.cddl') +p_prelude = Path(p_root, 'zcbor', 'prelude.cddl') class TestManifest(TestCase): diff --git a/zcbor/__init__.py b/zcbor/__init__.py deleted file mode 100644 index f3a76a5f..00000000 --- a/zcbor/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (c) 2021 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: Apache-2.0 -# - -from pathlib import Path - -from .zcbor import ( - CddlValidationError, - DataTranslator, -) - -script_path = Path(__file__).absolute().parents[0] -VERSION_path = Path(script_path, "VERSION") - -with open(VERSION_path, 'r', encoding="utf-8") as f: - __version__ = f.read() diff --git a/zcbor/cddl/prelude.cddl b/zcbor/prelude.cddl similarity index 100% rename from zcbor/cddl/prelude.cddl rename to zcbor/prelude.cddl diff --git a/zcbor/zcbor.py b/zcbor/zcbor.py index e9a4c931..f227434c 100755 --- a/zcbor/zcbor.py +++ b/zcbor/zcbor.py @@ -25,18 +25,20 @@ import sys from site import USER_BASE from textwrap import wrap, indent - +from importlib.metadata import version regex_cache = {} indentation = "\t" newl_ind = "\n" + indentation -P_SCRIPT = Path(__file__).absolute().parent -P_REPO_ROOT = Path(__file__).absolute().parents[1] -VERSION_path = Path(P_SCRIPT, "VERSION") -PRELUDE_path = Path(P_SCRIPT, "cddl", "prelude.cddl") +SCRIPT_PATH = Path(__file__).absolute().parent +PACKAGE_PATH = Path(__file__).absolute().parents[1] +PRELUDE_PATH = SCRIPT_PATH / "prelude.cddl" +VERSION_PATH = SCRIPT_PATH / "VERSION" +C_SRC_PATH = PACKAGE_PATH / "src" +C_INCLUDE_PATH = PACKAGE_PATH / "include" -__version__ = VERSION_path.read_text(encoding="utf-8").strip() +__version__ = VERSION_PATH.read_text(encoding="utf-8").strip() UINT8_MAX = 0xFF UINT16_MAX = 0xFFFF @@ -61,35 +63,6 @@ def getrp(pattern, flags=0): return regex_cache[pattern_key] -def is_relative_to(path1, path2): - try: - path1.relative_to(path2) - except ValueError: - return False - return True - - -# The root of the non-generated c code (/src and /include) -if Path(__file__).name in sys.argv[0]: - # Running the script directly in the repo. - c_code_root = P_REPO_ROOT -elif any((getrp(r"zcbor-.*\.egg").match(p) for p in P_SCRIPT.parts)): - # Installed via setup.py install - c_code_root = Path(P_SCRIPT.parent, "lib", "zcbor") -elif is_relative_to(P_SCRIPT, (Path(sys.prefix, "local"))): - # Installed via pip as root. - c_code_root = Path(sys.prefix, "local", "lib", "zcbor") -elif is_relative_to(P_SCRIPT, (Path(sys.prefix))): - # Installed via pip as root. - c_code_root = Path(sys.prefix, "lib", "zcbor") -elif is_relative_to(P_SCRIPT, (Path(USER_BASE))): - # Installed via pip as user. - c_code_root = Path(USER_BASE, "lib", "zcbor") -else: - # Don't know where the C code is. Assume we are in the repo. - c_code_root = P_REPO_ROOT - - # Size of "additional" field if num is encoded as int def sizeof(num): if num <= 23: @@ -2852,7 +2825,7 @@ def parse_args(): parent_parser.add_argument( "--no-prelude", required=False, action="store_true", default=False, help=f"""Exclude the standard CDDL prelude from the build. The prelude can be viewed at -{PRELUDE_path.relative_to(P_REPO_ROOT)} in the repo, or together with the script.""") +{PRELUDE_PATH.relative_to(PACKAGE_PATH)} in the repo, or together with the script.""") parent_parser.add_argument( "-v", "--verbose", required=False, action="store_true", default=False, help="Print more information while parsing CDDL and generating code.") @@ -3027,7 +3000,7 @@ def parse_args(): args = parser.parse_args() if not args.no_prelude: - args.cddl.append(open(PRELUDE_path, 'r', encoding="utf-8")) + args.cddl.append(open(PRELUDE_PATH, 'r', encoding="utf-8")) if hasattr(args, "decode") and not args.decode and not args.encode: parser.error("Please specify at least one of --decode or --encode.") @@ -3069,7 +3042,7 @@ def process_code(args): if "zcbor.py" in sys.argv[0]: git_args = ['git', 'rev-parse', '--verify', '--short', 'HEAD'] git_sha = Popen( - git_args, cwd=P_REPO_ROOT, stdout=PIPE).communicate()[0].decode('utf-8').strip() + git_args, cwd=PACKAGE_PATH, stdout=PIPE).communicate()[0].decode('utf-8').strip() else: git_sha = __version__ @@ -3115,8 +3088,8 @@ def add_mode_to_fname(filename, mode): file_header=args.file_header ) - c_code_dir = Path(c_code_root, "src") - h_code_dir = Path(c_code_root, "include") + c_code_dir = C_SRC_PATH + h_code_dir = C_INCLUDE_PATH if args.copy_sources: new_c_code_dir = out_c_parent