diff --git a/pyproject.toml b/pyproject.toml index 46e5902..87bad7b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,7 +17,7 @@ namespaces = true # ----------------------------------------- Project Metadata ------------------------------------- # [project] -version = "0.0.0.dev332" +version = "0.0.0.dev333" name = "ControlMan" dependencies = [ "packaging >= 23.2, < 24", @@ -38,6 +38,6 @@ dependencies = [ "JSONSchemata == 0.0.0.dev26", "VersionMan == 0.0.0.dev248", "HTMP == 0.0.0.dev5", - "LicenseMan == 0.0.0.dev12", + "LicenseMan == 0.0.0.dev13", ] requires-python = ">=3.10" diff --git a/requirements.txt b/requirements.txt index d52ae8d..03f3f0f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,4 +16,4 @@ MDit == 0.0.0.dev27 JSONSchemata == 0.0.0.dev26 VersionMan == 0.0.0.dev248 HTMP == 0.0.0.dev5 -LicenseMan == 0.0.0.dev12 \ No newline at end of file +LicenseMan == 0.0.0.dev13 \ No newline at end of file diff --git a/src/controlman/_data/schema/def/license-component.yaml b/src/controlman/_data/schema/def/license-component.yaml index ccae6f3..5b79842 100644 --- a/src/controlman/_data/schema/def/license-component.yaml +++ b/src/controlman/_data/schema/def/license-component.yaml @@ -161,7 +161,7 @@ properties: description: | XML header of the license. This is automatically filled when a supported `id` is selected. - $ref: https://jsonschemata.repodynamics.com/string/nonempty + type: string header_plain: title: Notice description: | @@ -174,13 +174,13 @@ properties: By default, the notice is added to the [main docstring](#ccc-pkg-file---init---py-docstring) of the package. ::: - $ref: https://jsonschemata.repodynamics.com/string/nonempty + type: string header_md: title: Markdown Header description: | Markdown header of the license. This is automatically filled when a supported `id` is selected. - $ref: https://jsonschemata.repodynamics.com/string/nonempty + type: string text_config: $ref: https://controlman.repodynamics.com/schema/license-component-config header_config: diff --git a/src/controlman/_data/schema/def/media-file.yaml b/src/controlman/_data/schema/def/media-file.yaml new file mode 100644 index 0000000..959f20b --- /dev/null +++ b/src/controlman/_data/schema/def/media-file.yaml @@ -0,0 +1,22 @@ +$id: https://controlman.repodynamics.com/schema/media-file +$schema: https://json-schema.org/draft/2020-12/schema +title: Media File +description: Media file. +type: object +additionalProperties: false +required: [ path, url ] +properties: + path: + title: Absolute Path + $ref: https://jsonschemata.repodynamics.com/path/posix/absolute-from-cwd + default: ${{ web.path.source }}/${{ .path_web }} + path_web: + title: Web Path + $ref: https://jsonschemata.repodynamics.com/path/posix/absolute-from-cwd + path_pkg: + title: Package Path + $ref: https://jsonschemata.repodynamics.com/path/posix/absolute-from-cwd + url: + title: URL + $ref: https://jsonschemata.repodynamics.com/url/https + default: ${{ repo.url.raw }}/${{ .path }} \ No newline at end of file diff --git a/src/controlman/_data/schema/def/pkg.yaml b/src/controlman/_data/schema/def/pkg.yaml index fc9f2c8..1bfbd1b 100644 --- a/src/controlman/_data/schema/def/pkg.yaml +++ b/src/controlman/_data/schema/def/pkg.yaml @@ -28,16 +28,19 @@ properties: type: object default: { } additionalProperties: false - required: [ root, source, import ] + required: [ root, source, source_rel, import ] properties: root: $ref: https://jsonschemata.repodynamics.com/path/posix/absolute-from-cwd - source: - $ref: https://jsonschemata.repodynamics.com/path/dir-name + source_rel: + $ref: https://jsonschemata.repodynamics.com/path/posix/absolute-from-cwd default: src + source: + $ref: https://jsonschemata.repodynamics.com/path/posix/absolute-from-cwd + default: ${{ .root }}/${{ .source_rel }} import: type: string - default: ${{ .root }}/${{ .source }}/${{ ..import_name }} + default: ${{ .source }}/${{ ..import_name }} name: title: Name of the Python distribution package. description: | @@ -249,6 +252,9 @@ properties: legal: XYZ Organization email: id: contact@xyz.org + license: + type: string + default: ${{ license.expression }} typed: description: Whether the package is typed type: boolean @@ -528,12 +534,13 @@ properties: - [Setuptools User Guide](https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html) - [Pip User Guide](https://pip.pypa.io/en/stable/reference/build-system/pyproject-toml/) type: object - default: { } + required: [ requires ] properties: requires: description: Build system requirements type: array - default: [ setuptools >= 72.1.0, versioningit >= 3.1.2 ] + examples: + - [ setuptools >= 72.1.0, versioningit >= 3.1.2 ] uniqueItems: true minItems: 1 items: @@ -541,14 +548,14 @@ properties: backend: description: Build backend type: string - default: setuptools.build_meta + examples: + - setuptools.build_meta tool: title: Build system definitions description: | Configurations for the build backend, e.g., [Setuptools]([SetupTools](https://setuptools.pypa.io/). type: object - default: { } additionalProperties: type: object properties: @@ -559,37 +566,25 @@ properties: include-package-data: title: Include package data type: boolean - default: true - zip-safe: - title: Zip-safe - type: boolean - default: false packages: title: Package definitions type: object - default: { } properties: find: title: Find packages type: object - default: { } properties: where: title: Where to find packages type: array - default: [ '${{ .......path.source }}' ] items: type: string namespaces: title: Include namespaces type: boolean - default: true cmdclass: title: Command classes type: object - default: - build_py: versioningit.cmdclass.build_py - sdist: versioningit.cmdclass.sdist versioningit: title: Versioningit type: object @@ -646,7 +641,7 @@ properties: default: true source-file: type: string - default: '${{ .....path.source }}/${{ .....import_name }}/__init__.py' + default: '${{ .....path.source_rel }}/${{ .....import_name }}/__init__.py' build-file: type: string default: '${{ .....import_name }}/__init__.py' @@ -662,7 +657,6 @@ properties: "distance": "{distance}", "commit_hash": "{revision}", }} - required: [ requires ] pypi: title: URLs of the project's PyPI package. type: object @@ -699,7 +693,7 @@ properties: readme: title: README file of the Conda package. default: - path: ${{ ...path.root }}/${{ ...path.source }}/README_conda.md + path: ${{ ...path.root }}/README_conda.md content: id: pypackit $ref: https://controlman.repodynamics.com/schema/docfile diff --git a/src/controlman/_data/schema/main.yaml b/src/controlman/_data/schema/main.yaml index 2c2a7ab..01da9a5 100644 --- a/src/controlman/_data/schema/main.yaml +++ b/src/controlman/_data/schema/main.yaml @@ -34,7 +34,7 @@ description: | maintenance, testing, and refactoring tasks. type: object -required: [ theme, repo, branch, control ] +required: [ repo, branch, control ] properties: name: title: Name @@ -248,7 +248,6 @@ properties: - `Apache-2.0`: [Apache License 2.0](https://choosealicense.com/licenses/apache-2.0/) - `MPL-2.0`: [Mozilla Public License 2.0](https://choosealicense.com/licenses/mpl-2.0/) - `Unlicense`: [The Unlicense](https://choosealicense.com/licenses/unlicense/) - default: AGPL-3.0-or-later $ref: https://jsonschemata.repodynamics.com/id/spdx-license component: type: object @@ -269,38 +268,23 @@ properties: $ref: https://controlman.repodynamics.com/schema/license-component-config header: $ref: https://controlman.repodynamics.com/schema/license-component-config - default: - text: &license_config_default_text - plain: - alts: - copyright: ${{ copyright.notice }} - author: ${{ team.owner.name.full }} - softwareName: ${{ name }} - description: ${{ title }} - contributor: ${{ team.owner.name.full }} - copyrightHolderAsIs: ${{ team.owner.name.full }} - initialDeveloper: ${{ team.owner.name.full }} - copyrightHolderLiability: ${{ team.owner.name.full }} - version: ${{ version }} - copyrightHolder0: ${{ team.owner.name.full }} - copyrightHolder1: ${{ team.owner.name.full }} - copyrightHolder2: ${{ team.owner.name.full }} - copyrightHolder3: ${{ team.owner.name.full }} - creator: ${{ team.owner.name.full }} - owner: ${{ team.owner.name.full }} - maintainer: ${{ team.owner.name.full }} - organization: ${{ team.owner.name.full }} - email: ${{ team.owner.email.url }} - softwareVersion: ${{ version }} - header: *license_config_default_text notice: title: Notice description: | Custom license notice. $ref: https://jsonschemata.repodynamics.com/string/nonempty - default: | - ${{ copyright.notice }} - SPDX-License-Identifier: ${{ license.expression }} + path: + type: object + additionalProperties: false + properties: + texts_plain: + type: array + items: + $ref: https://jsonschemata.repodynamics.com/path/posix/absolute-from-cwd + headers_plain: + type: array + items: + $ref: https://jsonschemata.repodynamics.com/path/posix/absolute-from-cwd copyright: title: Copyright description: | @@ -476,7 +460,6 @@ properties: which will then be used to automatically assign reviewers to pull requests based on the modified files. type: object - default: { } additionalProperties: false required: [ owners, path ] properties: @@ -933,26 +916,24 @@ properties: Configurations defining the visual theme of the project, such as colors, logos, and badges. type: object - default: { } additionalProperties: false - required: [ path, color, logo ] properties: - path: - title: Path - description: | - Path to the directory containing the project's media files. - - :::{admonition} Usage - :class: dropdown note - - The media directory is used to store images, logos, and other media files - that are displayed on the project's website and documentation files. - By default, they are included in the website builds - (see [`$.web.sphinx.config.html_static_path`](#ccc-web-sphinx-config-html_static_path)) - and thus can be directly used in the website, and linked to in the documentation. - ::: - default: docs/media - $ref: https://jsonschemata.repodynamics.com/path/posix/absolute-from-cwd +# path: +# title: Path +# description: | +# Path to the directory containing the project's media files. +# +# :::{admonition} Usage +# :class: dropdown note +# +# The media directory is used to store images, logos, and other media files +# that are displayed on the project's website and documentation files. +# By default, they are included in the website builds +# (see [`$.web.sphinx.config.html_static_path`](#ccc-web-sphinx-config-html_static_path)) +# and thus can be directly used in the website, and linked to in the documentation. +# ::: +# default: ${{ web.path.root }}/${{ web.path.source}}/_media +# $ref: https://jsonschemata.repodynamics.com/path/posix/absolute-from-cwd color: title: Colors description: | @@ -967,16 +948,10 @@ properties: title: Primary description: Primary color of the project. $ref: https://controlman.repodynamics.com/schema/themed-color - default: - light: '#0F3460' - dark: '#3A98B9' secondary: title: Secondary description: Secondary color of the project. $ref: https://controlman.repodynamics.com/schema/themed-color - default: - light: '#562B08' - dark: '#E3651D' logo: title: Logo description: | @@ -986,80 +961,63 @@ properties: type: object additionalProperties: false default: { } - required: [ icon, simple, full ] properties: - root: - $ref: https://jsonschemata.repodynamics.com/path/posix/absolute-from-cwd - default: ${{ web.path.root }}/${{ web.path.source}}/_media/logo icon: title: Icon description: Favicon (website icon) of the project. - $ref: https://jsonschemata.repodynamics.com/path/posix/absolute-from-cwd - default: ${{ theme.logo.root }}/favicon.svg + $ref: https://controlman.repodynamics.com/schema/media-file simple: title: Simple description: Simple versions of the project's logo, to be used in small spaces. type: object additionalProperties: false - required: [ light, dark ] - default: { } properties: light: title: Light description: Light-themed version of the simple logo. - $ref: https://jsonschemata.repodynamics.com/path/posix/absolute-from-cwd - default: ${{ theme.logo.root }}/simple_light.svg + $ref: https://controlman.repodynamics.com/schema/media-file dark: title: Dark description: Dark-themed version of the simple logo. - $ref: https://jsonschemata.repodynamics.com/path/posix/absolute-from-cwd - default: ${{ theme.logo.root }}/simple_dark.svg + $ref: https://controlman.repodynamics.com/schema/media-file raster: title: Raster description: | Raster version of the simple logo, to be used in places where vector graphics are not supported. - $ref: https://jsonschemata.repodynamics.com/path/posix/absolute-from-cwd - default: ${{ theme.logo.root }}/simple_light.png + $ref: https://controlman.repodynamics.com/schema/media-file ascii: title: ASCII description: | ASCII art version of the simple logo, to be used in command-line interfaces and text-based environments. - $ref: https://jsonschemata.repodynamics.com/path/posix/absolute-from-cwd - default: ${{ theme.logo.root }}/simple.txt + $ref: https://controlman.repodynamics.com/schema/media-file full: title: Full description: Full versions of the project's logo, to be used in large spaces. type: object additionalProperties: false - required: [ light, dark ] - default: { } properties: light: title: Light description: Light-themed version of the full logo. - $ref: https://jsonschemata.repodynamics.com/path/posix/absolute-from-cwd - default: ${{ theme.logo.root }}/full_light.svg + $ref: https://controlman.repodynamics.com/schema/media-file dark: title: Dark description: Dark-themed version of the full logo. - $ref: https://jsonschemata.repodynamics.com/path/posix/absolute-from-cwd - default: ${{ theme.logo.root }}/full_dark.svg + $ref: https://controlman.repodynamics.com/schema/media-file raster: title: Raster description: | Raster version of the full logo, to be used in places where vector graphics are not supported. - $ref: https://jsonschemata.repodynamics.com/path/posix/absolute-from-cwd - default: ${{ theme.logo.root }}/full_light.png + $ref: https://controlman.repodynamics.com/schema/media-file ascii: title: ASCII description: | ASCII art version of the full logo, to be used in command-line interfaces and text-based environments. - $ref: https://jsonschemata.repodynamics.com/path/posix/absolute-from-cwd - default: ${{ theme.logo.root }}/full.txt + $ref: https://controlman.repodynamics.com/schema/media-file badge: title: Badge description: | @@ -1140,7 +1098,6 @@ properties: Configurations for [community health files](https://docs.github.com/en/communities/setting-up-your-project-for-healthy-contributions/creating-a-default-community-health-file). type: object - default: { } additionalProperties: false properties: contributing: @@ -1333,9 +1290,12 @@ properties: root: $ref: https://jsonschemata.repodynamics.com/path/posix/absolute-from-cwd default: docs/website - source: - $ref: https://jsonschemata.repodynamics.com/path/dir-name + source_rel: + $ref: https://jsonschemata.repodynamics.com/path/posix/absolute-from-cwd default: source + source: + $ref: https://jsonschemata.repodynamics.com/path/posix/absolute-from-cwd + default: ${{ .root }}/${{ .source_rel }} to_root: title: Relative path from the source directory to the root of the repository. default: ../../.. @@ -3419,6 +3379,28 @@ properties: - v required: [ prefix ] required: [ version ] + file: + type: object + additionalProperties: false + properties: + duplicate: + type: object + additionalProperties: + type: object + additionalProperties: false + required: [ destination ] + properties: + source: + $ref: https://jsonschemata.repodynamics.com/path/posix/absolute-from-cwd + sources: + type: array + items: + $ref: https://jsonschemata.repodynamics.com/path/posix/absolute-from-cwd + destination: + $ref: https://jsonschemata.repodynamics.com/path/posix/absolute-from-cwd + oneOf: + - required: [ source ] + - required: [ sources ] # CI control: title: Configurations for the project's control center. diff --git a/src/controlman/center_manager.py b/src/controlman/center_manager.py index 2a65348..ace657b 100644 --- a/src/controlman/center_manager.py +++ b/src/controlman/center_manager.py @@ -176,12 +176,27 @@ def apply_changes(self) -> None: f.write(f"{generated_file.content.strip()}\n") if generated_file.change in (DynamicFileChangeType.MOVED, DynamicFileChangeType.MOVED_MODIFIED): filepath_before_abs.unlink(missing_ok=True) + + for duplicate in self._data_before.get("file.duplicate", {}).values(): + if "source" in duplicate: + self._path_root.joinpath(duplicate["destination"]).unlink(missing_ok=True) + else: + for source in duplicate["sources"]: + self._path_root.joinpath(duplicate["destination"]).joinpath(_Path(source).stem).unlink(missing_ok=True) + for duplicate in self._data.get("file.duplicate", {}).values(): + if "source" in duplicate: + _shutil.copy2(self._path_root.joinpath(duplicate["source"]), self._path_root.joinpath(duplicate["destination"])) + else: + for source in duplicate["sources"]: + destination_path = self._path_root.joinpath(duplicate["destination"]).joinpath(_Path(source).stem) + destination_path.parent.mkdir(parents=True, exist_ok=True) + _shutil.copy2(self._path_root.joinpath(source), destination_path) return def _compare_dirs(self): def compare_source(main_key: str, root_path: str, root_path_before: str): - source_name, source_name_before = self._get_dirpath(f"{main_key}.path.source") + source_name, source_name_before = self._get_dirpath(f"{main_key}.path.source_rel") source_path = f"{root_path}/{source_name}" if root_path else None source_path_before_real = f"{root_path_before}/{source_name_before}" if root_path_before and source_name_before else None change = self._compare_dir_paths(source_path, source_path_before_real) @@ -200,7 +215,7 @@ def compare_import(main_key: str, source_path: str, source_path_before: str, sou return self._dirs dirs = [] to_apply = [] - for path_key in ("theme", "control"): + for path_key in ("control", ): path, path_before, status = self._compare_dir(f"{path_key}.path") dirs.append( _DynamicDir( diff --git a/src/controlman/data_gen/__init__.py b/src/controlman/data_gen/__init__.py index 4698678..d8e32b8 100644 --- a/src/controlman/data_gen/__init__.py +++ b/src/controlman/data_gen/__init__.py @@ -49,11 +49,10 @@ def generate( github_api=github_api, ).generate() if data.get("web"): - web_data_source = data_before if data_before["web.path.source"] else data + web_path_source = data_before["web.path.source"] or data.fill("web.path.source") _WebDataGenerator( data=data, - source_path=git_manager.repo_path / web_data_source["web.path.root"] / web_data_source[ - "web.path.source"], + source_path=git_manager.repo_path / web_path_source, ).generate() _RepoDataGenerator( data=data, diff --git a/src/controlman/data_gen/main.py b/src/controlman/data_gen/main.py index 23e044f..f98c943 100644 --- a/src/controlman/data_gen/main.py +++ b/src/controlman/data_gen/main.py @@ -140,16 +140,19 @@ def _license(self): json_path="license.expression", data=self._data(), ) + path_texts = [] + path_headers = [] for spdx_ids, spdx_typ in ((license_ids, "license"), (exception_ids, "exception")): func = _spdx.license if spdx_typ == "license" else _spdx.exception class_ = _spdx.SPDXLicense if spdx_typ == "license" else _spdx.SPDXLicenseException for spdx_id in spdx_ids: user_data = self._data.setdefault("license.component", {}).setdefault(spdx_id, {}) + user_data_path = user_data.setdefault("path", {}) path_text = normalize_license_filename( - user_data["path"]["text_plain"] if user_data else f"LICENSE-{spdx_id}.md" + user_data_path.get("text_plain", f"LICENSE-{spdx_id}.md") ) path_header = normalize_license_filename( - user_data["path"]["header_plain"] if user_data else f"COPYRIGHT-{spdx_id}.md" + user_data_path.get("header_plain", f"COPYRIGHT-{spdx_id}.md") ) source_data = self._cache.get("license", spdx_id) if source_data: @@ -157,13 +160,10 @@ def _license(self): else: licence = func(spdx_id) self._cache.set("license", spdx_id, licence.raw_data) + header_xml = (licence.header_xml_str or "") if spdx_typ == "license" else "" out_data = { "type": spdx_typ, "custom": False, - "path": { - "text_plain": path_text, - "header_plain": path_header, - }, "id": licence.id, "name": licence.name, "reference_num": licence.reference_number, @@ -174,7 +174,7 @@ def _license(self): "json": licence.url_json, "cross_refs": licence.url_cross_refs, "repo_text_plain": f"{self._data["repo.url.blob"]}/{path_text}", - "repo_header_plain": f"{self._data["repo.url.blob"]}/{path_header}", + "repo_header_plain": f"{self._data["repo.url.blob"]}/{path_header}" if header_xml else "", }, "version_added": licence.version_added or "", "deprecated": licence.deprecated, @@ -185,7 +185,11 @@ def _license(self): "comments": licence.comments or "", "trove_classifier": _spdx.trove_classifier(licence.id) or "", "text_xml": licence.text_xml_str, - "header_xml": licence.header_xml_str if spdx_typ == "license" else "", + "header_xml": header_xml, + } + user_data_path |= { # Overwrite with normalized paths + "text_plain": path_text, + "header_plain": path_header if header_xml else "", } _ps.update.dict_from_addon( data=user_data, @@ -195,6 +199,11 @@ def _license(self): raise_duplicates=False, raise_type_mismatch=True, ) + path_texts.append(path_text) + if header_xml: + path_headers.append(path_header) + self._data["license.path.texts_plain"] = path_texts + self._data["license.path.headers_plain"] = path_headers return def _copyright(self): diff --git a/src/controlman/data_validator.py b/src/controlman/data_validator.py index 33fa947..72351c4 100644 --- a/src/controlman/data_validator.py +++ b/src/controlman/data_validator.py @@ -75,8 +75,8 @@ def dir_paths(self): path_keys = [] for dirpath_key in ( "control.path", - # "local.path", - # "theme.path", + "local.cache.path", + "local.report.path" "pkg.path.root", "test.path.root", "web.path.root", @@ -105,6 +105,40 @@ def dir_paths(self): json_path=rel_key, data=self._data, ) + for pkg_path_key in ("pkg.path", "test.path", "web.path"): + path_data = self._data[pkg_path_key] + if not path_data: + continue + path_root = _Path(path_data["root"]) + path_source = _Path(path_data["source"]) + if not path_source.is_relative_to(path_root): + raise _exception.load.ControlManSchemaValidationError( + source=self._source, + problem=f"Directory path '{path_source}' defined at '{pkg_path_key}.source' is not relative to" + f"directory path '{path_root}' defined at '{pkg_path_key}.root'.", + json_path=f"{pkg_path_key}.source", + data=self._data, + ) + source_rel = path_source.relative_to(path_root) + if source_rel != _Path(path_data["source_rel"]): + raise _exception.load.ControlManSchemaValidationError( + source=self._source, + problem=f"Directory path '{path_data['source_rel']}' defined at '{pkg_path_key}.source_rel' does not match" + f"the relative path '{source_rel}' between '{path_root}' and '{path_source}'.", + json_path=f"{pkg_path_key}.source_rel", + data=self._data, + ) + if pkg_path_key == "web.path": + continue + path_import = _Path(path_data["import"]) + if not path_import.is_relative_to(path_source): + raise _exception.load.ControlManSchemaValidationError( + source=self._source, + problem=f"Directory path '{path_import}' defined at '{pkg_path_key}.import' is not relative to" + f"directory path '{path_source}' defined at '{pkg_path_key}.source'.", + json_path=f"{pkg_path_key}.import", + data=self._data, + ) return def branch_names(self): diff --git a/src/controlman/file_gen/config.py b/src/controlman/file_gen/config.py index 8723f2c..71308fd 100644 --- a/src/controlman/file_gen/config.py +++ b/src/controlman/file_gen/config.py @@ -127,8 +127,8 @@ def web_toc(self) -> list[DynamicFile]: toc_file = { "type": DynamicFileType.WEB_CONFIG, "subtype": ("toc", "Table of Contents"), - "path": f"{self._data["web.path.root"]}/{self._data["web.path.source"]}/{self._data["web.toc.path"]}" if self._data["web.toc"] else None, - "path_before": f"{self._data_before["web.path.root"]}/{self._data_before["web.path.source"]}/{self._data_before["web.toc.path"]}" if self._data_before["web.toc"] else None, + "path": f"{self._data["web.path.source"]}/{self._data["web.toc.path"]}" if self._data["web.toc"] else None, + "path_before": f"{self._data_before["web.path.source"]}/{self._data_before["web.toc.path"]}" if self._data_before["web.toc"] else None, } toc = self._data["web.toc"] if not toc: diff --git a/src/controlman/file_gen/python.py b/src/controlman/file_gen/python.py index d1677c8..7464048 100644 --- a/src/controlman/file_gen/python.py +++ b/src/controlman/file_gen/python.py @@ -43,11 +43,11 @@ def generate(self, typ: Literal["pkg", "test"], pyproject_tool_config: dict | st self._pkg = _ps.NestedDict(self._data[typ]) self._pkg_before = _ps.NestedDict(self._data_before[typ] or {}) self._path_root = _Path(self._data[f"{typ}.path.root"]) - self._path_src = self._path_root / self._data[f"{typ}.path.source"] + self._path_src = self._path_root / self._data[f"{typ}.path.source_rel"] self._path_import = self._path_src / self._pkg["import_name"] if self._data_before[f"{typ}.path"]: self._path_root_before = _Path(self._data_before[f"{typ}.path.root"]) - self._path_src_before = self._path_root_before / self._data_before[f"{typ}.path.source"] + self._path_src_before = self._path_root_before / self._data_before[f"{typ}.path.source_rel"] self._path_import_before = self._path_src_before / self._pkg_before["import_name"] return ( self.requirements() @@ -241,7 +241,7 @@ def pyproject_build_system(self) -> dict: def pyproject_project(self) -> dict: readme = _Path(self._pkg["readme.path"]).name if self._pkg["readme.path"] else None - license = {"file": _Path(self._data["license.path"]).name} if self._data["license.path"] else None + license = {"text": self._data["license.expression"]} if self._data["license.expression"] else None data = { "name": ("str", self._pkg["name"]), "description": ("str", self._pkg["description"]),