diff --git a/.github/workflows/code-qa.yml b/.github/workflows/code-qa.yml index 0674ef5f..a9ef472a 100644 --- a/.github/workflows/code-qa.yml +++ b/.github/workflows/code-qa.yml @@ -31,34 +31,29 @@ jobs: python_version: "3.10" steps: - uses: actions/checkout@v4 - - name: Set up Python ${{matrix.python_version}} - uses: actions/setup-python@v5 with: - cache: pip - cache-dependency-path: pyproject.toml + fetch-depth: 0 + - uses: astral-sh/setup-uv@v4 + with: + enable-cache: true + cache-dependency-glob: "**/pyproject.toml" python-version: ${{matrix.python_version}} - - name: Upgrade Pip - run: python -m pip install -U pip - - name: Install test dependencies - run: python -m pip install '.[test]' - name: Run unit tests - run: python -m pytest --cov-report=term --cov=capellambse --rootdir=. + run: uv run -m pytest --cov-report=term --cov=capellambse --rootdir=. pre-commit: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 with: - cache: pip - cache-dependency-path: pyproject.toml + fetch-depth: 0 + - uses: astral-sh/setup-uv@v4 + with: + enable-cache: true + cache-dependency-glob: "**/pyproject.toml" python-version: "3.12" - - name: Upgrade pip - run: python -m pip install -U pip - - name: Install pre-commit - run: python -m pip install 'pre-commit' - name: Run Pre-Commit - run: pre-commit run --all-files + run: uv run pre-commit run --all-files build: name: Build wheel @@ -67,18 +62,15 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Setup Python - uses: actions/setup-python@v5 + - uses: astral-sh/setup-uv@v4 with: - cache: pip - cache-dependency-path: pyproject.toml + enable-cache: true + cache-dependency-glob: "**/pyproject.toml" python-version: "3.12" - - name: Install dependencies - run: python -m pip install -U pip build twine - name: Build packages - run: python -m build + run: uv build - name: Verify packages - run: python -m twine check dist/* + run: uvx twine check dist/* - name: Upload artifacts uses: actions/upload-artifact@v4 with: @@ -120,15 +112,11 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/setup-python@v5 + - uses: astral-sh/setup-uv@v4 with: - cache: pip - cache-dependency-path: pyproject.toml + enable-cache: true + cache-dependency-glob: "**/pyproject.toml" python-version: "3.12" - - name: Upgrade pip - run: python -m pip install -U pip - - name: Install dependencies - run: python -m pip install jupyter '.[docs,png]' capellambse-context-diagrams - name: Run example notebooks run: | jq --version @@ -142,7 +130,7 @@ jobs: } for file in docs/source/examples/*.ipynb; do old_outputs="$(celloutputs "$file")" - if ! jupyter nbconvert --to notebook --execute "$file" --output "${file##*/}" --ExecutePreprocessor.timeout "${NOTEBOOK_TIMEOUT_SEC:-300}"; then + if ! uv run --all-extras jupyter nbconvert --to notebook --execute "$file" --output "${file##*/}" --ExecutePreprocessor.timeout "${NOTEBOOK_TIMEOUT_SEC:-300}"; then ok=false continue fi @@ -160,17 +148,13 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/setup-python@v5 + - uses: astral-sh/setup-uv@v4 with: - cache: pip - cache-dependency-path: pyproject.toml + enable-cache: true + cache-dependency-glob: "**/pyproject.toml" python-version: "3.12" - - name: Upgrade pip - run: python -m pip install -U pip - - name: Install dependencies - run: | - sudo apt-get install -y pandoc - python -m pip install '.[docs]' + - name: Install system-level dependencies + run: sudo apt-get install -y pandoc - name: Auto-generate APIDOC sources run: make -C docs apidoc - name: Create docs diff --git a/.gitignore b/.gitignore index 636a7727..4da6eda5 100644 --- a/.gitignore +++ b/.gitignore @@ -162,6 +162,9 @@ dmypy.json # Cython debug symbols cython_debug/ +# don't commit lock files for library projects +uv.lock + # Mac garbage .DS_Store diff --git a/README.md b/README.md index 5da04f7b..10db8538 100644 --- a/README.md +++ b/README.md @@ -182,20 +182,18 @@ pip install capellambse Development ----------- -To set up a development environment, clone the project and install it into a -virtual environment. +Use [uv](https://docs.astral.sh/uv/) to set up a local development environment. ```bash git clone https://github.com/DSD-DBS/py-capellambse cd py-capellambse -python -m venv .venv +uv sync +uv run pre-commit install +# You may need to explicitly activate the project venv +# to make code completion and tools available: source .venv/bin/activate.sh # for Linux / Mac .venv\Scripts\activate # for Windows - -pip install -U pip pre-commit -pip install -e '.[docs,test]' -pre-commit install ``` The example notebooks (see above) are verified during CI, to ensure their @@ -206,9 +204,8 @@ Use the following command to start a Jupyter server, which can be used to develop and re-run the example notebooks: ```bash -pip install jupyter capellambse cd docs/source/examples -CAPELLAMBSE_UUID_SEED=0 jupyter-notebook +CAPELLAMBSE_UUID_SEED=0 uv run jupyter lab ``` > [!NOTE] @@ -220,10 +217,6 @@ CAPELLAMBSE_UUID_SEED=0 jupyter-notebook If your browser did not open automatically, follow the instructions in the terminal to start it manually. -Once in the browser, simply click on the [01 -Introduction.ipynb](https://dsd-dbs.github.io/py-capellambse/examples/01%20Introduction.html) -notebook to start! - Current limitations ------------------- diff --git a/docs/Makefile b/docs/Makefile index f6d33492..1c14d671 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -11,21 +11,23 @@ SPHINXBUILD ?= sphinx-build SOURCEDIR = source BUILDDIR = build +UV_PYTHON = 3.12 + # Put it first so that "make" without argument is like "make help". help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + @uv run --no-dev --group docs-build $(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) .PHONY: help Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + @uv run --no-dev --group docs-build $(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) # Auto-generate API documentation apidoc: - sphinx-apidoc --module-first --output-dir source/code --force .. \ + uv run --no-dev --group docs-build sphinx-apidoc --module-first --output-dir source/code --force .. \ ../capellambse/aird/diagram.py \ ../capellambse/extensions/reqif/elements.py \ ../capellambse/repl.py \ diff --git a/docs/source/examples/01 Introduction.ipynb b/docs/source/examples/01 Introduction.ipynb index c91dd827..3f79cc54 100644 --- a/docs/source/examples/01 Introduction.ipynb +++ b/docs/source/examples/01 Introduction.ipynb @@ -229,40 +229,14 @@ "id": "30e17ac3", "metadata": {}, "source": [ - "We can also turn the above data into a table, for example \"actor function allocation\", using `pandas`.\n", + "Using the `pandas` library, we can also easily export the data above in tabular format.\n", "\n", - "For this, we first make sure pandas itself is installed, as well as an extension we'll use later." + "For that, first build up the table as pandas DataFrame:" ] }, { "cell_type": "code", "execution_count": 4, - "id": "400e483d-eca7-4fdd-a9e0-71467e1af8d8", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "%pip install -q pandas openpyxl" - ] - }, - { - "cell_type": "markdown", - "id": "acbe1247-e1a1-4da8-a95a-7b41f4836937", - "metadata": {}, - "source": [ - "Now we can use it together with `capellambse`:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, "id": "833220d0", "metadata": {}, "outputs": [ @@ -344,7 +318,7 @@ "5 LAF 1 " ] }, - "execution_count": 5, + "execution_count": 4, "metadata": {}, "output_type": "execute_result" } @@ -368,12 +342,12 @@ "id": "6e04c482", "metadata": {}, "source": [ - "and any `pandas.DataFrame` can always be turned into an Excel Spreadsheet, just like that:" + "This DataFrame can then be processed as usual. For example, to export it as Excel Spreadsheet:" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "id": "10af24a2", "metadata": {}, "outputs": [], @@ -407,7 +381,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 6, "id": "eb5f8747", "metadata": {}, "outputs": [ @@ -436,7 +410,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 7, "id": "71bf090e", "metadata": { "scrolled": true @@ -547,7 +521,7 @@ "[47] " ] }, - "execution_count": 8, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -575,7 +549,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 8, "id": "cc09fd22", "metadata": {}, "outputs": [ @@ -589,7 +563,7 @@ "" ] }, - "execution_count": 9, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } @@ -608,7 +582,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 9, "id": "b5699e75-2e4c-4b8e-81af-51be00ff6dc3", "metadata": {}, "outputs": [ @@ -641,7 +615,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 10, "id": "7798e7f0-61eb-4395-99f2-2cc6dbd46ff1", "metadata": {}, "outputs": [], @@ -651,7 +625,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 11, "id": "93a2e9cf-e599-4c3d-96b7-d6c590e74b16", "metadata": {}, "outputs": [ @@ -688,7 +662,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 12, "id": "cf91c71a", "metadata": {}, "outputs": [ @@ -703,7 +677,7 @@ "[0] " ] }, - "execution_count": 13, + "execution_count": 12, "metadata": {}, "output_type": "execute_result" } @@ -726,7 +700,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 13, "id": "b5751a0d", "metadata": {}, "outputs": [ @@ -745,7 +719,7 @@ "[2] " ] }, - "execution_count": 14, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } diff --git a/docs/source/examples/02 Intro to Physical Architecture API.ipynb b/docs/source/examples/02 Intro to Physical Architecture API.ipynb index a5b11939..56f64d27 100644 --- a/docs/source/examples/02 Intro to Physical Architecture API.ipynb +++ b/docs/source/examples/02 Intro to Physical Architecture API.ipynb @@ -7,31 +7,7 @@ "source": [ "# Introduction to Physical Architecture API\n", "\n", - "**Note**: In this notebook we will use `pandas` dataframes library to construct and visualize tables, **if you don't have pandas installed** in the current environment you may want to do so by running the cell below." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "4c181cfb", - "metadata": { - "editable": true, - "slideshow": { - "slide_type": "" - }, - "tags": [] - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "%pip install -q pandas" + "**Note**: In this notebook, we will use the `pandas` library to construct and visualize tables, so make sure it is installed before running it locally." ] }, { @@ -44,7 +20,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "id": "1386d2f7", "metadata": { "scrolled": true @@ -78,7 +54,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "id": "8b555976", "metadata": {}, "outputs": [ @@ -92,7 +68,7 @@ "" ] }, - "execution_count": 3, + "execution_count": 2, "metadata": {}, "output_type": "execute_result" } @@ -114,7 +90,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "id": "711574aa", "metadata": { "scrolled": true @@ -163,7 +139,7 @@ "[16] " ] }, - "execution_count": 4, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" } @@ -185,7 +161,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "id": "21ef5c4d", "metadata": {}, "outputs": [ @@ -296,7 +272,7 @@ ".xtype = 'org.polarsys.capella.core.data.pa:PhysicalComponent'" ] }, - "execution_count": 5, + "execution_count": 4, "metadata": {}, "output_type": "execute_result" } @@ -317,7 +293,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "id": "63ff286c", "metadata": {}, "outputs": [], @@ -341,7 +317,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 6, "id": "6d8ff25c", "metadata": {}, "outputs": [ @@ -536,7 +512,7 @@ "16 " ] }, - "execution_count": 7, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } @@ -559,7 +535,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 7, "id": "01644c9d", "metadata": {}, "outputs": [ @@ -588,7 +564,7 @@ "[7] " ] }, - "execution_count": 8, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -612,7 +588,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 8, "id": "70abf6f9", "metadata": {}, "outputs": [ @@ -631,7 +607,7 @@ "[2] " ] }, - "execution_count": 9, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } @@ -657,7 +633,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 9, "id": "faa00eb0", "metadata": {}, "outputs": [ @@ -743,7 +719,7 @@ "7 ISP Network Mail server" ] }, - "execution_count": 10, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } @@ -774,7 +750,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 10, "id": "e48d4998", "metadata": {}, "outputs": [ @@ -807,7 +783,7 @@ "[9] " ] }, - "execution_count": 11, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } diff --git a/pyproject.toml b/pyproject.toml index 01fe2aec..99a5b344 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,25 +48,6 @@ Homepage = "https://github.com/DSD-DBS/py-capellambse" Documentation = "https://dsd-dbs.github.io/py-capellambse" [project.optional-dependencies] -docs = [ - "furo", - "ipython", - "jinja2", - "nbsphinx>=0.9.5", - "sphinx>=8.0.2", - "sphinx-argparse-cli", - "sphinx-autodoc-typehints", - "tomli; python_version<'3.11'", -] - -test = [ - "click", - "jinja2>=3.1.3", - "pytest", - "pytest-cov", - "requests-mock", -] - cli = [ "click>=8.1.7", "jinja2>=3.1.3", @@ -101,6 +82,37 @@ pvmt = "capellambse.extensions.pvmt:init" reqif = "capellambse.extensions.reqif:init" validation = "capellambse.extensions.validation:init" +[dependency-groups] +dev = [ + "cairosvg>=2.5.2", + "capellambse-context-diagrams>=0.6.1", + "click>=8.1.7", + "jinja2>=3.1.3", + "jupyterlab>=4.3.3", + "pandas>=2.2.3", + "pre-commit>=4.0.1", + "pyls-isort>=0.2.2", + "pylsp-mypy>=0.6.9", + "pytest-cov>=6.0.0", + "pytest>=8.3.4", + "python-lsp-server>=1.12.0", + "requests-mock>=1.12.1", + "ruff>=0.8.2", + "mypypp>=0.1.1", + "xlsxwriter>=3.2.0", +] + +docs-build = [ + "furo>=2024.8.6", + "ipython>=8.30.0", + "jinja2>=3.1.3", + "nbsphinx>=0.9.5", + "sphinx>=8.1.3", + "sphinx-argparse-cli>=1.19.0", + "sphinx-autodoc-typehints>=2.5.0", + "tomli>=2.2.1 ; python_full_version < '3.11'", +] + [tool.coverage.run] branch = true command_line = "-m pytest"