diff --git a/.github/workflows/build-python-client.yml b/.github/workflows/build-python-client.yml index 09a93e3c..31385626 100644 --- a/.github/workflows/build-python-client.yml +++ b/.github/workflows/build-python-client.yml @@ -18,24 +18,27 @@ jobs: uses: actions/setup-python@v2 with: python-version: 3.10.14 + - name: install uv + uses: yezz123/setup-uv@v4 - name: Setup Python environment run: | git status make devenv source .venv/bin/activate - - name: Process tag + - name: Git tag to VERSION if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') run: | source .venv/bin/activate version=$(echo ${{ github.ref }} | grep -oP '(?<=refs/tags/v)\d+\.\d+\.\d+') python scripts/is_semantic_version.py "${version}" - echo "${version}" > clients/python/client/VERSION - - name: Generate client + echo "${version}" > clients/python/VERSION + - name: Auto-generate python_client and build distributions run: | source .venv/bin/activate cd clients/python - make install-dev - make dist-ci + uv pip install -r requirements/dev.txt + make python-client + make dist - name: Set variables id: variables run: | @@ -70,6 +73,8 @@ jobs: uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} + - name: install uv + uses: yezz123/setup-uv@v4 - name: Pip cache uses: actions/cache@v3 with: @@ -90,6 +95,7 @@ jobs: python -m pip install clients/python/artifacts/dist/${{needs.build.outputs.osparc}} --find-links=clients/python/artifacts/dist cd clients/python make install-unit-test + uv pip list | grep osparc pytest -v --ignore=/artifacts/client --ignore=test/e2e test-latest: @@ -106,6 +112,8 @@ jobs: uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} + - name: install uv + uses: yezz123/setup-uv@v4 - name: Pip cache uses: actions/cache@v3 with: @@ -122,8 +130,8 @@ jobs: run: | python -m venv .venv source .venv/bin/activate - python -m pip install pytest - python -m pip install clients/python/artifacts/dist/${{needs.build.outputs.osparc}} --find-links=clients/python/artifacts/dist + uv pip install pytest + uv pip install clients/python/artifacts/dist/${{needs.build.outputs.osparc}} --find-links=clients/python/artifacts/dist cd clients/python make install-unit-test pytest -v --ignore=/artifacts/client --ignore=test/e2e diff --git a/.gitignore b/.gitignore index 7cee8a15..a8219e3d 100644 --- a/.gitignore +++ b/.gitignore @@ -78,7 +78,14 @@ tmp* # auto generated python code /clients/python/artifacts/* -/clients/python/client/build/* -/clients/python/client/osparc/data/openapi.json +/clients/python/src/build/* +/clients/python/src/osparc/data/openapi.json +/clients/python/VERSION /clients/python/test/e2e/pytest.ini -/clients/python/client/VERSION + + +# key-words in filename to ignore them +*secret* +*ignore* +!.dockerignore +!.gitignore diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 226cb491..fe724515 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -27,16 +27,13 @@ repos: types: ["file"] - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.0.282 + rev: v0.6.2 hooks: - id: ruff - args: [ --fix, --exit-non-zero-on-fix ] - - repo: https://github.com/pycqa/isort - rev: 5.12.0 - hooks: - - id: isort - name: isort (python) - args: ["--profile", "black"] + args: + - --fix + - --exit-non-zero-on-fix + - id: ruff-format - repo: https://github.com/psf/black rev: 23.7.0 hooks: diff --git a/.vscode/launch.template.json b/.vscode/launch.template.json new file mode 100644 index 00000000..f1063d42 --- /dev/null +++ b/.vscode/launch.template.json @@ -0,0 +1,34 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python Debugger: Current File", + "type": "debugpy", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal" + }, + { + "name": "Python: Run Test", + "type": "debugpy", + "request": "launch", + "module": "pytest", + "args": [ + "--ff", + "--log-cli-level=INFO", + "--pdb", + "--setup-show", + "-sx", + "-vv", + "--asyncio-mode=auto", + "${file}" + ], + "cwd": "${workspaceFolder}", + "console": "integratedTerminal", + "justMyCode": false + } + ] +} diff --git a/.vscode/settings.template.json b/.vscode/settings.template.json index 78bd88dd..503f68bb 100644 --- a/.vscode/settings.template.json +++ b/.vscode/settings.template.json @@ -1,4 +1,15 @@ { + "files.associations": { + ".*rc": "ini", + ".env*": "ini", + "**/requirements/*.in": "pip-requirements", + "**/requirements/*.txt": "pip-requirements", + "*logs.txt": "log", + "*.logs*": "log", + "*Makefile": "makefile", + "docker-compose*.yml": "dockercompose", + "Dockerfile*": "dockerfile" + }, "pylint.args": [ "--rcfile=clients/python/.pylintrc" ] diff --git a/clients/python/LICENSE b/LICENSE similarity index 100% rename from clients/python/LICENSE rename to LICENSE diff --git a/Makefile b/Makefile index d5ecfc48..e49530af 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,13 @@ include ./scripts/common.Makefile PYTHON_DIR := $(CLIENTS_DIR)/python + +.vscode/%.json: .vscode/%.template.json + $(if $(wildcard $@), \ + @echo "WARNING ##### $< is newer than $@ ####"; diff -uN $@ $<; false;,\ + @echo "WARNING ##### $@ does not exist, cloning $< as $@ ############"; cp $< $@) + + .PHONY: info info: ## general information # system @@ -13,30 +20,32 @@ info: ## general information @which python @pip list # API - @echo ' title : ' $(shell bash $(SCRIPTS_DIR)/jq.bash -r .info.title $(REPO_ROOT)/api/openapi.json) - @echo ' version : ' $(shell bash $(SCRIPTS_DIR)/jq.bash -r .info.version $(REPO_ROOT)/api/openapi.json) + @echo ' title : $(shell bash $(SCRIPTS_DIR)/jq.bash -r .info.title $(REPO_ROOT)/api/openapi.json)' + @echo ' version : $(shell bash $(SCRIPTS_DIR)/jq.bash -r .info.version $(REPO_ROOT)/api/openapi.json)' # nox @echo nox --list-session -.venv: - @python3 --version - python3 -m venv $@ +.venv: .check-uv-installed + @uv venv $@ ## upgrading tools to latest version in $(shell python3 --version) - $@/bin/pip3 --quiet install --upgrade \ - pip \ + @uv pip --quiet install --upgrade \ + pip~=24.0 \ wheel \ - setuptools - @$@/bin/pip3 list --verbose + setuptools \ + uv + @uv pip list + .PHONY: devenv -devenv: .venv ## create a python virtual environment with dev tools (e.g. linters, etc) - $ /dev/null || rm .git/hooks/pre-commit - -_git_clean_args := -dx --force --exclude=.vscode --exclude=TODO.md --exclude=.venv --exclude=.python-version --exclude="*keep*" - -.check-clean: - @git clean -n $(_git_clean_args) - @echo -n "Are you sure? [y/N] " && read ans && [ $${ans:-N} = y ] - @echo -n "$(shell whoami), are you REALLY sure? [y/N] " && read ans && [ $${ans:-N} = y ] - - -clean: .check-clean ## cleans all unversioned files in project and temp files create by this makefile - # Cleaning unversioned - @git clean $(_git_clean_args) diff --git a/clients/python/Makefile b/clients/python/Makefile index f9e69b47..d2c7e04b 100644 --- a/clients/python/Makefile +++ b/clients/python/Makefile @@ -1,7 +1,8 @@ -include ../../scripts/common.Makefile -PYTHON_DIR := $(CLIENTS_DIR)/python -ARTIFACTS_DIR := $(PYTHON_DIR)/artifacts -CONTAINER_API_JSON := /local/$(REL_API_JSON_PATH) + +CLIENTS_PYTHON_DIR := $(CLIENTS_DIR)/python +ARTIFACTS_DIR := $(CLIENTS_PYTHON_DIR)/artifacts +CONTAINER_API_JSON := /local/$(OPENAPI_SPECS_JSON_REL_PATH) GENERATOR := python UID := $(shell id -u) ADDITIONAL_PROPS := \ @@ -12,28 +13,20 @@ ADDITIONAL_PROPS := \ packageUrl=$(shell bash $(SCRIPTS_DIR)/jq.bash -r .homepage $(REPO_ROOT)/api/config.json)\ projectName=osparc_client ADDITIONAL_PROPS := $(foreach prop,$(ADDITIONAL_PROPS),$(strip $(prop))) -VERSION_FILE := $(PYTHON_DIR)/client/VERSION +VERSION_FILE := $(CLIENTS_PYTHON_DIR)/VERSION .PHONY: generate_version -client/VERSION: +VERSION: @bash $(SCRIPTS_DIR)/generate_version.bash > $(VERSION_FILE) .PHONY: artifacts_dir artifacts_dir: - -mkdir $(ARTIFACTS_DIR) + -mkdir --parents $(ARTIFACTS_DIR) -.PHONY: postprocess-build -postprocess-build: - black $(PYTHON_DIR)/artifacts/client/*.py - @-rm -f $(PYTHON_DIR)/artifacts/client/README.md - @echo "Please visit our [website](https://itisfoundation.github.io/osparc-simcore-clients/#/) for documentation" > $(PYTHON_DIR)/artifacts/client/README.md - @-rm -rf $(PYTHON_DIR)/client/osparc/data/ - @-mkdir $(PYTHON_DIR)/client/osparc/data/ - @cp $(REPO_ROOT)/api/openapi.json $(PYTHON_DIR)/client/osparc/data/ # Generation of Python client .PHONY: python-client -python-client: client/VERSION validate-api-specification artifacts_dir ## generate python client +python-client: VERSION validate-api-specification artifacts_dir ## auto-generate python client $(eval VERSION := $(shell cat $(VERSION_FILE))) -rm -r $(ARTIFACTS_DIR)/client docker run --rm --user $(UID):$(UID)\ @@ -51,7 +44,7 @@ python-client: client/VERSION validate-api-specification artifacts_dir ## genera $(MAKE) postprocess-build .PHONY: python-client-from-templates -python-client-from-templates: client/VERSION validate-api-specification artifacts_dir ## generate python client using templates in a specified directory (usage: 'make python-client-from-templates -e TEMPLATE_DIR=path/to/templates') +python-client-from-templates: VERSION validate-api-specification artifacts_dir ## generate python client using templates in a specified directory (usage: 'make python-client-from-templates -e TEMPLATE_DIR=path/to/templates') $(if $(TEMPLATE_DIR),,$(error The TEMPLATE_DIR environment variable must be set)) @echo "Using template-dir: $(TEMPLATE_DIR)" $(eval VERSION := $(shell cat $(VERSION_FILE))) @@ -102,29 +95,41 @@ _check_venv_active: # checking whether virtual environment was activated @python3 -c "import sys; assert sys.base_prefix!=sys.prefix" + + +.PHONY: .install-dev-reqs +.install-dev-reqs: _check_venv_active + uv pip install -r $(CLIENTS_PYTHON_DIR)/requirements/dev.txt + .PHONY: install-dev -install-dev: _check_venv_active ## install packages for development - pip install -r requirements/dev.txt +install-dev: _check_venv_active .install-dev-reqs python-client ## installs osparc_client and osparc in edit mode + uv pip install -e artifacts/client + uv pip install -e . + uv pip list .PHONY: install-unit-test install-unit-test: _check_venv_active ## install packages for unit testing client - pip install -r $(PYTHON_DIR)/requirements/unit-test.txt + uv pip install -r $(CLIENTS_PYTHON_DIR)/requirements/unit-test.txt .PHONY: install-e2e-test install-e2e-test: _check_venv_active ## install packages for e2e testing client - pip install -r $(PYTHON_DIR)/requirements/e2e-test.txt + uv pip install -r $(CLIENTS_PYTHON_DIR)/requirements/e2e-test.txt .PHONY: install-doc -install-doc: _check_venv_active install-dev ## install packages for generating documentation - pip install -r $(PYTHON_DIR)/requirements/doc.txt +install-doc: _check_venv_active .install-dev-reqs ## install packages for generating documentation + uv pip install -r $(CLIENTS_PYTHON_DIR)/requirements/doc.txt + + .PHONY: pylint pylint: _check_venv_active ## runs linter (only to check errors. SEE .pylintrc enabled) - pylint --rcfile "$(PYTHON_DIR)/.pylintrc" -v "$(ARTIFACTS_DIR)/client" + pylint --rcfile "$(CLIENTS_PYTHON_DIR)/.pylintrc" -v src/osparc .PHONY: mypy -mypy: $(SCRIPTS_DIR)/mypy.bash $(PYTHON_DIR)/mypy.ini ## runs mypy python static type-checker on this services's code. Use AFTER make install-* - @$(SCRIPTS_DIR)/mypy.bash client +mypy: $(SCRIPTS_DIR)/mypy.bash $(CLIENTS_PYTHON_DIR)/mypy.ini ## runs mypy python static type-checker on this services's code. Use AFTER make install-* + @$(SCRIPTS_DIR)/mypy.bash src/osparc + + .PHONY: test-dev test-dev: _check_venv_active ## runs tests during development @@ -135,31 +140,39 @@ test-dev: _check_venv_active ## runs tests during development --failed-first \ --durations=10 \ --pdb \ - $(PYTHON_DIR)/test/test_osparc + $(CLIENTS_PYTHON_DIR)/test/test_osparc + + .PHONY: dist -dist: artifacts_dir ## builds distribution wheel - # installs pypa/build - python -m pip install build - # Build a binary wheel and a source tarball +dist: artifacts_dir ## builds distribution wheel for `osparc_client` and `osparc` packages -> $(ARTIFACTS_DIR)/dist + # Installing pypa/build ... + uv pip install build + + # Building a binary wheel and a source tarball ... python -m build --sdist --wheel $(ARTIFACTS_DIR)/client - python -m build --sdist --wheel $(PYTHON_DIR)/client + python -m build --sdist --wheel $(CLIENTS_PYTHON_DIR) + + # Copying to artifacts/dist ... @-rm -rf $(ARTIFACTS_DIR)/dist @mkdir $(ARTIFACTS_DIR)/dist - @cp $$(ls artifacts/client/dist/*.whl) $(ARTIFACTS_DIR)/dist - @cp $$(ls client/dist/*.whl) $(ARTIFACTS_DIR)/dist - @cp client/VERSION $(ARTIFACTS_DIR)/dist + @cp -v $$(ls artifacts/client/dist/*.whl) $(ARTIFACTS_DIR)/dist + @cp -v $$(ls dist/*.whl) $(ARTIFACTS_DIR)/dist + @cp -v VERSION $(ARTIFACTS_DIR)/dist + -.PHONY: dist-ci -dist-ci: python-client dist ## build wheel and tar ball in a single command +.PHONY: postprocess-build +postprocess-build: + black $(ARTIFACTS_DIR)/client/*.py + @-rm -f $(ARTIFACTS_DIR)/client/README.md + @echo "Please visit our [website](https://itisfoundation.github.io/osparc-simcore-clients/#/) for documentation" > $(ARTIFACTS_DIR)/client/README.md + @-rm -rf $(CLIENTS_PYTHON_DIR)/src/osparc/data/ + @-mkdir $(CLIENTS_PYTHON_DIR)/src/osparc/data/ + @cp $(REPO_ROOT)/api/openapi.json $(CLIENTS_PYTHON_DIR)/src/osparc/data/ -.PHONY: build-n-install-osparc-dev -build-n-install-osparc-dev: python-client ## install the built osparc package in editable mode - python -m pip install -e artifacts/client - python -m pip install -e client/ -## TEST --------------------------------------------------------------------------------- +## e2e TEST --------------------------------------------------------------------------------- GH_TOKEN_FILE := $(wildcard $(HOME)/.gh-token) ifneq ($(GH_TOKEN_FILE),) @@ -191,9 +204,9 @@ e2e-shell: guard-GH_TOKEN ## shell for running e2e tests @echo "WARNING ##### $@ does not exist, cloning $< as $@ ############"; cp $< $@) .PHONY: image -image: client/VERSION ## builds image $(APP_NAME):version - docker build -f Dockerfile -t $(APP_NAME):$(shell cat $(VERSION_FILE)) $(CURDIR) +image: VERSION ## builds image $(APP_NAME):version + docker build -f docker/Dockerfile -t $(APP_NAME):$(shell cat $(VERSION_FILE)) $(CURDIR) .PHONY: shell -shell: client/VERSION ## runs container and opens bash shell +shell: VERSION ## runs container and opens bash shell docker run -it $(APP_NAME):$(shell cat $(VERSION_FILE)) /bin/bash diff --git a/clients/python/Dockerfile b/clients/python/docker/Dockerfile similarity index 100% rename from clients/python/Dockerfile rename to clients/python/docker/Dockerfile diff --git a/clients/python/mypy.ini b/clients/python/mypy.ini deleted file mode 100644 index 6cbe6258..00000000 --- a/clients/python/mypy.ini +++ /dev/null @@ -1,27 +0,0 @@ -# Global options -[mypy] -check_untyped_defs = True -disallow_any_generics = False -# disallow_untyped_defs: if True, it enforces things like `def __init__(self) -> CLASSNAME` or `def test_() -> None` which does not worth the effort -disallow_untyped_defs = False -follow_imports = silent -# ignore_missing_imports: removes all the missing imports stuff from external libraries which is annoying to the least -ignore_missing_imports = True -namespace_packages = True -no_implicit_reexport = True -plugins = pydantic.mypy, sqlalchemy.ext.mypy.plugin -python_version = 3.10 -show_column_numbers = True -show_error_context = False -strict_optional = False -warn_redundant_casts = True -warn_return_any = True -warn_unused_configs = True -warn_unused_ignores = True - -# SEE https://docs.pydantic.dev/mypy_plugin/#plugin-settings -[pydantic-mypy] -init_forbid_extra = True -init_typed = True -warn_required_dynamic_aliases = True -warn_untyped_fields = True diff --git a/clients/python/pyproject.toml b/clients/python/pyproject.toml new file mode 100644 index 00000000..4a5e0b3c --- /dev/null +++ b/clients/python/pyproject.toml @@ -0,0 +1,37 @@ +[tool.mypy] +# Global options +check_untyped_defs = true +disallow_any_generics = false +# disallow_untyped_defs: if True, it enforces things like `def __init__(self) -> CLASSNAME` or `def test_() -> None` which does not worth the effort +disallow_untyped_defs = false +follow_imports = "silent" +# ignore_missing_imports: removes all the missing imports stuff from external libraries which is annoying to the least +ignore_missing_imports = true +namespace_packages = true +no_implicit_reexport = true +plugins = ["pydantic.mypy", "sqlalchemy.ext.mypy.plugin"] +python_version = "3.10" +show_column_numbers = true +show_error_context = false +strict_optional = false +warn_redundant_casts = true +warn_return_any = true +warn_unused_configs = true +warn_unused_ignores = true + +[tool.pydantic-mypy] +# SEE https://docs.pydantic.dev/mypy_plugin/#plugin-settings +init_forbid_extra = true +init_typed = true +warn_required_dynamic_aliases = true +warn_untyped_fields = true + + +[tool.pytest.ini_options] +# SEE https://docs.pytest.org/en/7.1.x/reference/customize.html#pyproject-toml +testpaths = [ + "test", +] +# SEE https://pytest-asyncio.readthedocs.io/en/stable/concepts.html#test-discovery-modes +asyncio_mode = "auto" +required_plugins = "pytest-asyncio" diff --git a/clients/python/requirements/unit-test.txt b/clients/python/requirements/unit-test.txt index 7a7be230..91a9931b 100644 --- a/clients/python/requirements/unit-test.txt +++ b/clients/python/requirements/unit-test.txt @@ -1,4 +1,5 @@ -r ../../../requirements.txt +black faker pipdeptree pipreqs diff --git a/clients/python/client/setup.py b/clients/python/setup.py similarity index 76% rename from clients/python/client/setup.py rename to clients/python/setup.py index e37e2410..23ce764c 100644 --- a/clients/python/client/setup.py +++ b/clients/python/setup.py @@ -1,31 +1,38 @@ +""" +To install the library, run the following + +python setup.py install + + +prerequisites: + - setuptools + - VERSION + +SEE http://pypi.python.org/pypi/setuptools +""" + from pathlib import Path from setuptools import find_packages, setup # noqa: H301 VERSION_FILE: Path = Path(__file__).parent / "VERSION" -assert VERSION_FILE.is_file() +assert VERSION_FILE.is_file(), "Did you forget `make VERSION`?" NAME = "osparc" VERSION = VERSION_FILE.read_text() -# To install the library, run the following -# -# python setup.py install -# -# prerequisite: setuptools -# http://pypi.python.org/pypi/setuptools REQUIRES = [ - f"osparc_client=={VERSION}", "httpx", - "tqdm>=4.48.0", "nest_asyncio", - "tenacity", - "pydantic>=2.0.0", - "pydantic-settings", "packaging", + "pydantic-settings", + "pydantic", + "tenacity", + "tqdm>=4.48.0", + f"osparc_client=={VERSION}", ] -setup( +SETUP = dict( name=NAME, version=VERSION, description="osparc.io web API", @@ -33,9 +40,9 @@ author_email="support@osparc.io", url="https://itisfoundation.github.io/osparc-simcore-clients/", install_requires=REQUIRES, - packages=find_packages(exclude=["test", "tests"]), include_package_data=True, - package_dir={"": "."}, + packages=find_packages(where="src"), + package_dir={"": "src"}, package_data={ "": [ "data/openapi.json", @@ -51,8 +58,12 @@ classifiers=[ "Development Status :: 3 - Alpha", "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3", - "Operating System :: OS Independent", "Natural Language :: English", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", ], ) + + +if __name__ == "__main__": + setup(**SETUP) diff --git a/clients/python/client/osparc/__init__.py b/clients/python/src/osparc/__init__.py similarity index 95% rename from clients/python/client/osparc/__init__.py rename to clients/python/src/osparc/__init__.py index 6e0451b4..7d8f94b0 100644 --- a/clients/python/client/osparc/__init__.py +++ b/clients/python/src/osparc/__init__.py @@ -3,7 +3,7 @@ from typing import List, Tuple import nest_asyncio -from osparc_client import ( # APIs; API client; models +from osparc_client import ( ApiException, ApiKeyError, ApiTypeError, @@ -25,9 +25,6 @@ OpenApiException, Profile, ProfileUpdate, -) -from osparc_client import RunningState as TaskStates -from osparc_client import ( # APIs; API client; models Solver, SolverPort, UserRoleEnum, @@ -36,6 +33,7 @@ ValidationError, __version__, ) +from osparc_client import RunningState as TaskStates from packaging.version import Version from ._api_client import ApiClient diff --git a/clients/python/client/osparc/_api_client.py b/clients/python/src/osparc/_api_client.py similarity index 96% rename from clients/python/client/osparc/_api_client.py rename to clients/python/src/osparc/_api_client.py index a30c51d5..f96b4da1 100644 --- a/clients/python/client/osparc/_api_client.py +++ b/clients/python/src/osparc/_api_client.py @@ -4,7 +4,7 @@ from osparc_client import Configuration from pydantic import ValidationError -from ._models import ConfigurationModel +from ._settings import ConfigurationModel class ApiClient(_ApiClient): diff --git a/clients/python/client/osparc/_exceptions.py b/clients/python/src/osparc/_exceptions.py similarity index 100% rename from clients/python/client/osparc/_exceptions.py rename to clients/python/src/osparc/_exceptions.py diff --git a/clients/python/client/osparc/_files_api.py b/clients/python/src/osparc/_files_api.py similarity index 100% rename from clients/python/client/osparc/_files_api.py rename to clients/python/src/osparc/_files_api.py diff --git a/clients/python/client/osparc/_http_client.py b/clients/python/src/osparc/_http_client.py similarity index 100% rename from clients/python/client/osparc/_http_client.py rename to clients/python/src/osparc/_http_client.py diff --git a/clients/python/client/osparc/_info.py b/clients/python/src/osparc/_info.py similarity index 100% rename from clients/python/client/osparc/_info.py rename to clients/python/src/osparc/_info.py diff --git a/clients/python/client/osparc/_models.py b/clients/python/src/osparc/_settings.py similarity index 90% rename from clients/python/client/osparc/_models.py rename to clients/python/src/osparc/_settings.py index 24c74243..9d2e0a54 100644 --- a/clients/python/client/osparc/_models.py +++ b/clients/python/src/osparc/_settings.py @@ -6,8 +6,9 @@ class ParentProjectInfo(BaseSettings): - """Information a project cann pass onto its "children" (i.e. projects - 'spawned' through the api-server)""" + """This is information a project can pass onto its "children" (i.e. projects + 'spawned' through the api-server) + """ x_simcore_parent_project_uuid: Optional[str] = Field( alias="OSPARC_STUDY_ID", default=None diff --git a/clients/python/client/osparc/_solvers_api.py b/clients/python/src/osparc/_solvers_api.py similarity index 98% rename from clients/python/client/osparc/_solvers_api.py rename to clients/python/src/osparc/_solvers_api.py index 6a3d6aa2..5f0169d6 100644 --- a/clients/python/client/osparc/_solvers_api.py +++ b/clients/python/src/osparc/_solvers_api.py @@ -5,7 +5,7 @@ from osparc_client import SolversApi as _SolversApi from ._api_client import ApiClient -from ._models import ParentProjectInfo +from ._settings import ParentProjectInfo from ._utils import ( _DEFAULT_PAGINATION_LIMIT, _DEFAULT_PAGINATION_OFFSET, diff --git a/clients/python/client/osparc/_studies_api.py b/clients/python/src/osparc/_studies_api.py similarity index 99% rename from clients/python/client/osparc/_studies_api.py rename to clients/python/src/osparc/_studies_api.py index f35fe3ba..5ea3baa5 100644 --- a/clients/python/client/osparc/_studies_api.py +++ b/clients/python/src/osparc/_studies_api.py @@ -11,7 +11,7 @@ from ._api_client import ApiClient from ._http_client import AsyncHttpClient -from ._models import ParentProjectInfo +from ._settings import ParentProjectInfo from ._utils import ( _DEFAULT_PAGINATION_LIMIT, _DEFAULT_PAGINATION_OFFSET, diff --git a/clients/python/client/osparc/_utils.py b/clients/python/src/osparc/_utils.py similarity index 100% rename from clients/python/client/osparc/_utils.py rename to clients/python/src/osparc/_utils.py diff --git a/clients/python/client/osparc/api.py b/clients/python/src/osparc/api.py similarity index 100% rename from clients/python/client/osparc/api.py rename to clients/python/src/osparc/api.py diff --git a/clients/python/test/e2e/_utils.py b/clients/python/test/e2e/_utils.py index bf702d52..978b2cd9 100644 --- a/clients/python/test/e2e/_utils.py +++ b/clients/python/test/e2e/_utils.py @@ -7,8 +7,8 @@ import pytest from packaging.version import Version -_python_dir: Path = Path(__file__).parent.parent.parent -assert _python_dir.is_dir() +_clients_python_dir: Path = Path(__file__).parent.parent.parent +assert _clients_python_dir.is_dir() def osparc_dev_features_enabled() -> bool: @@ -17,9 +17,9 @@ def osparc_dev_features_enabled() -> bool: def repo_version() -> Version: subprocess.run( - "make client/VERSION", cwd=_python_dir.resolve(), shell=True + "make client/VERSION", cwd=_clients_python_dir.resolve(), shell=True ).check_returncode() - version_file: Path = Path(_python_dir / "client" / "VERSION") + version_file: Path = Path(_clients_python_dir / "VERSION") assert version_file.is_file() return Version(version_file.read_text()) diff --git a/clients/python/test/e2e/conftest.py b/clients/python/test/e2e/conftest.py index 6c97bcdb..be0b8728 100644 --- a/clients/python/test/e2e/conftest.py +++ b/clients/python/test/e2e/conftest.py @@ -19,7 +19,7 @@ from pydantic import ByteSize try: - from osparc._models import ConfigurationModel + from osparc._settings import ConfigurationModel except ImportError: pass diff --git a/clients/python/test/pytest.ini b/clients/python/test/pytest.ini deleted file mode 100644 index 3830826d..00000000 --- a/clients/python/test/pytest.ini +++ /dev/null @@ -1,3 +0,0 @@ -[pytest] -required_plugins = pytest-asyncio -asyncio_mode = auto diff --git a/clients/python/test/test_osparc/__init__.py b/clients/python/test/test_osparc/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/clients/python/test/test_osparc/conftest.py b/clients/python/test/test_osparc/conftest.py index 67dfd069..ecf5face 100644 --- a/clients/python/test/test_osparc/conftest.py +++ b/clients/python/test/test_osparc/conftest.py @@ -1,3 +1,8 @@ +# pylint: disable=redefined-outer-name +# pylint: disable=unused-argument +# pylint: disable=unused-variable +# pylint: disable=too-many-arguments + import osparc import pytest from faker import Faker diff --git a/clients/python/test/test_osparc/test_AsyncHttpClient.py b/clients/python/test/test_osparc/test_async_http_client.py similarity index 100% rename from clients/python/test/test_osparc/test_AsyncHttpClient.py rename to clients/python/test/test_osparc/test_async_http_client.py diff --git a/clients/python/test/test_osparc/test_basic.py b/clients/python/test/test_osparc/test_basic.py index 20b172a1..d8f75070 100644 --- a/clients/python/test/test_osparc/test_basic.py +++ b/clients/python/test/test_osparc/test_basic.py @@ -5,11 +5,11 @@ from typing import Any, Dict, List, Set import osparc -import osparc._models +import osparc._settings import pydantic import pytest -_PYTHON_DIR: Path = Path(__file__).parent.parent.parent +_CLIENTS_PYTHON_DIR: Path = Path(__file__).parent.parent.parent def test_get_api(): @@ -21,37 +21,49 @@ def test_dependencies(tmp_path: Path): """ Ensure packages which are imported in osparc are also specified in setup.py """ - # get imported packages + # get in-code imported packages import_file: Path = tmp_path / "imported_packages.txt" - source_package: Path = _PYTHON_DIR / "client" / "osparc" + source_package: Path = _CLIENTS_PYTHON_DIR / "src" / "osparc" assert source_package.is_dir() - cmd: list[str] = [ - "pipreqs", - "--savepath", - str(import_file.resolve()), - "--mode", - "no-pin", - ] - output = subprocess.run( - cmd, cwd=source_package, stdout=subprocess.PIPE, stderr=subprocess.PIPE + + subprocess.run( + [ + "pipreqs", + "--savepath", + str(import_file.resolve()), + "--mode", + "no-pin", + ], + cwd=source_package, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + check=True, + ) + import_dependencies: Set[str] = set( + _.replace(".egg", "") for _ in import_file.read_text().splitlines() ) - assert output.returncode == 0 - import_dependencies: Set[str] = set(import_file.read_text().splitlines()) # generate requirements file based on installed osparc - cmd: list[str] = [ - "pipdeptree", - "-p", - "osparc", - "--json", - ] - output = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + output = subprocess.run( + [ + "pipdeptree", + "-p", + "osparc", + "--json", + ], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + check=False, + ) assert output.returncode == 0 - dep_tree: List[Dict[str, Any]] = json.loads(output.stdout) - dep_tree = [elm for elm in dep_tree if elm["package"]["key"] == "osparc"] - assert len(dep_tree) == 1 - install_dependencies: Set(str) = { - dep["package_name"].replace("-", "_") for dep in dep_tree[0]["dependencies"] + dependency_tree: List[Dict[str, Any]] = json.loads(output.stdout) + dependency_tree = [ + node for node in dependency_tree if node["package"]["key"] == "osparc" + ] + assert len(dependency_tree) == 1 + install_dependencies: Set[str] = { + dep["package_name"].replace("-", "_") + for dep in dependency_tree[0]["dependencies"] } msg: str = ( "imported dependencies not specified " @@ -67,11 +79,11 @@ def test_parent_project_validation(faker, valid: bool): if valid: os.environ["OSPARC_STUDY_ID"] = f"{faker.uuid4()}" os.environ["OSPARC_NODE_ID"] = f"{faker.uuid4()}" - parent_info = osparc._models.ParentProjectInfo() + parent_info = osparc._settings.ParentProjectInfo() assert parent_info.x_simcore_parent_project_uuid is not None assert parent_info.x_simcore_parent_node_id is not None else: os.environ["OSPARC_STUDY_ID"] = f"{faker.text()}" os.environ["OSPARC_NODE_ID"] = f"{faker.text()}" with pytest.raises(pydantic.ValidationError): - _ = osparc._models.ParentProjectInfo() + _ = osparc._settings.ParentProjectInfo() diff --git a/scripts/common.Makefile b/scripts/common.Makefile index 0af5e252..5d78058b 100644 --- a/scripts/common.Makefile +++ b/scripts/common.Makefile @@ -18,8 +18,8 @@ OPENAPI_GENERATOR_TAG := v0 OPENAPI_GENERATOR_IMAGE := $(OPENAPI_GENERATOR_NAME):$(OPENAPI_GENERATOR_TAG) # openapi specification -REL_API_JSON_PATH := api/openapi.json -ABS_API_JSON_PATH := $(REPO_ROOT)/$(REL_API_JSON_PATH) +OPENAPI_SPECS_JSON_REL_PATH := api/openapi.json +OPENAPI_SPECS_JSON_ABS_PATH := $(REPO_ROOT)/$(OPENAPI_SPECS_JSON_REL_PATH) GIT_USER_ID := ITISFoundation GIT_CLIENT_REPO_ID := osparc-simcore-clients @@ -35,16 +35,14 @@ comma := , help: ## help on rule's targets @awk --posix 'BEGIN {FS = ":.*?## "} /^[[:alpha:][:space:]_-]+:.*?## / {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) -# Validate openapi specification -------------------------------------------------------------- validate-api-specification: ## validates openapi-specification @docker run --rm \ --volume "$(REPO_ROOT):/local" \ - $(OPENAPI_GENERATOR_IMAGE) validate --input-spec /local/$(REL_API_JSON_PATH) + $(OPENAPI_GENERATOR_IMAGE) validate --input-spec /local/$(OPENAPI_SPECS_JSON_REL_PATH) -# check variables - -# Check that given variables are set and all have non-empty values, +# +# check variables: check that given variables are set and all have non-empty values, # die with an error otherwise. # # Params: @@ -55,3 +53,35 @@ guard-%: echo "Environment variable $* not set"; \ exit 1; \ fi + +## CLEAN ------------------------------------------------------------------------------- + +.PHONY: clean-hooks +clean-hooks: ## Uninstalls git pre-commit hooks + @-pre-commit uninstall 2> /dev/null || rm .git/hooks/pre-commit + +_git_clean_args := -dx --force --exclude=.vscode --exclude=TODO.md --exclude=.venv --exclude=.python-version --exclude="*keep*" + +.check-clean: + @git clean -n $(_git_clean_args) + @echo -n "Are you sure? [y/N] " && read ans && [ $${ans:-N} = y ] + @echo -n "$(shell whoami), are you REALLY sure? [y/N] " && read ans && [ $${ans:-N} = y ] + + +clean: .check-clean ## cleans all unversioned files in project and temp files create by this makefile + # Cleaning unversioned + @git clean $(_git_clean_args) + + + +.PHONY: .check-uv-installed +.check-uv-installed: + @echo "Checking if 'uv' is installed..." + @if ! command -v uv >/dev/null 2>&1; then \ + curl -LsSf https://astral.sh/uv/install.sh | sh; \ + else \ + printf "\033[32m'uv' is installed. Version: \033[0m"; \ + uv --version; \ + fi + # upgrading uv + -@uv self --quiet update diff --git a/scripts/generate_version.bash b/scripts/generate_version.bash index a086d27c..a819ef7d 100644 --- a/scripts/generate_version.bash +++ b/scripts/generate_version.bash @@ -14,6 +14,9 @@ release_version=$(echo "${release_info}" | grep -oP '(?<=refs/tags/v)\d+\.\d+\.\ release_commit=$(echo "${release_info}" | grep -oE '^[[:alnum:]]+') current_commit=$(git rev-parse HEAD) +# +# Determines how many commits since the last release and adds that as `post` index. +# WARNING: we should change Version Scheme to https://packaging.python.org/en/latest/specifications/version-specifiers/#version-scheme merge_base=$(git merge-base "${release_commit}" "${current_commit}") n_commits_to_merge_base=$(git rev-list --count "${merge_base}".."${current_commit}") echo -n "${release_version}.post${n_commits_to_merge_base}"