chore(deps): drop poetry as a dependency in the python automate template (#40)
* chore: bump specklepy * refactor: requirements files * refactor: dockerfile * refactor: update GitHub actions * refactor: update devcontainer config * docs: updated `README` * docs: better explanations * docs: wording * chore: dependabot config * docs: keeping things in sync * fix: skip pip upgrade * refactor: updated devcontainer * refactor: toml instead of requirements * refactor: update docker * docs: updated `README` * chore: classifiers does nothing * chore: wrap up the v3 transition --------- Co-authored-by: Gergő Jedlicska <gergo@jedlicska.com>
This commit is contained in:
@@ -4,9 +4,6 @@
|
|||||||
"name": "Python 3",
|
"name": "Python 3",
|
||||||
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
||||||
"image": "mcr.microsoft.com/devcontainers/python:1-3.11-bullseye",
|
"image": "mcr.microsoft.com/devcontainers/python:1-3.11-bullseye",
|
||||||
"features": {
|
|
||||||
"ghcr.io/devcontainers-contrib/features/poetry:2": {}
|
|
||||||
},
|
|
||||||
|
|
||||||
"remoteEnv": {
|
"remoteEnv": {
|
||||||
"SPECKLE_TOKEN": "foobar"
|
"SPECKLE_TOKEN": "foobar"
|
||||||
@@ -22,7 +19,7 @@
|
|||||||
// "forwardPorts": [],
|
// "forwardPorts": [],
|
||||||
|
|
||||||
// Use 'postCreateCommand' to run commands after the container is created.
|
// Use 'postCreateCommand' to run commands after the container is created.
|
||||||
"postCreateCommand": "cp .env.example .env && POETRY_VIRTUALENVS_IN_PROJECT=true poetry install --no-root",
|
"postCreateCommand": "cp .env.example .env && python -m venv .venv && . .venv/bin/activate && pip install --upgrade pip && pip install .[dev]",
|
||||||
|
|
||||||
// Configure tool-specific properties.
|
// Configure tool-specific properties.
|
||||||
"customizations": {
|
"customizations": {
|
||||||
@@ -40,4 +37,4 @@
|
|||||||
|
|
||||||
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
||||||
// "remoteUser": "root"
|
// "remoteUser": "root"
|
||||||
}
|
}
|
||||||
@@ -4,3 +4,7 @@ updates:
|
|||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "daily"
|
interval: "daily"
|
||||||
|
- package-ecosystem: "pip"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
name: 'build and deploy Speckle functions'
|
name: "build and deploy Speckle functions"
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- '*'
|
- "*"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
publish-automate-function-version: # make sure the action works on a clean machine without building
|
publish-automate-function-version: # make sure the action works on a clean machine without building
|
||||||
@@ -14,15 +14,10 @@ jobs:
|
|||||||
- uses: actions/checkout@v4.1.7
|
- uses: actions/checkout@v4.1.7
|
||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: '3.11'
|
python-version: "3.11"
|
||||||
- name: Install poetry
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
pip install poetry==1.8.4 &&
|
pip install .[dev]
|
||||||
poetry config virtualenvs.create false &&
|
|
||||||
poetry config virtualenvs.in-project false &&
|
|
||||||
poetry config installer.parallel true
|
|
||||||
- name: Restore dependencies
|
|
||||||
run: poetry install --no-root
|
|
||||||
- name: Extract functionInputSchema
|
- name: Extract functionInputSchema
|
||||||
id: extract_schema
|
id: extract_schema
|
||||||
run: |
|
run: |
|
||||||
@@ -34,4 +29,4 @@ jobs:
|
|||||||
speckle_token: ${{ secrets.SPECKLE_FUNCTION_TOKEN }}
|
speckle_token: ${{ secrets.SPECKLE_FUNCTION_TOKEN }}
|
||||||
speckle_function_id: ${{ secrets.SPECKLE_FUNCTION_ID }}
|
speckle_function_id: ${{ secrets.SPECKLE_FUNCTION_ID }}
|
||||||
speckle_function_input_schema_file_path: ${{ env.FUNCTION_SCHEMA_FILE_NAME }}
|
speckle_function_input_schema_file_path: ${{ env.FUNCTION_SCHEMA_FILE_NAME }}
|
||||||
speckle_function_command: 'python -u main.py run'
|
speckle_function_command: "python -u main.py run"
|
||||||
|
|||||||
+7
-7
@@ -1,16 +1,16 @@
|
|||||||
# We use the official Python 3.11 image as our base image and will add our code to it. For more details, see https://hub.docker.com/_/python
|
# We use the official Python 3.11 image as our base image and will add our code to it. For more details, see https://hub.docker.com/_/python
|
||||||
FROM python:3.11-slim
|
FROM python:3.11-slim
|
||||||
|
|
||||||
# We install poetry to generate a list of dependencies which will be required by our application
|
|
||||||
RUN pip install poetry==1.8.4
|
|
||||||
|
|
||||||
# We set the working directory to be the /home/speckle directory; all of our files will be copied here.
|
# We set the working directory to be the /home/speckle directory; all of our files will be copied here.
|
||||||
WORKDIR /home/speckle
|
WORKDIR /home/speckle
|
||||||
|
|
||||||
|
# Copy pyproject.toml first to leverage Docker layer caching
|
||||||
|
COPY pyproject.toml /home/speckle/
|
||||||
|
|
||||||
|
# Install the required Python packages (production dependencies only)
|
||||||
|
RUN pip install --no-cache-dir .
|
||||||
|
|
||||||
# Copy all of our code and assets from the local directory into the /home/speckle directory of the container.
|
# Copy all of our code and assets from the local directory into the /home/speckle directory of the container.
|
||||||
# We also ensure that the user 'speckle' owns these files, so it can access them
|
# We also ensure that the user 'speckle' owns these files, so it can access them
|
||||||
# This assumes that the Dockerfile is in the same directory as the rest of the code
|
# This assumes that the Dockerfile is in the same directory as the rest of the code
|
||||||
COPY . /home/speckle
|
COPY . /home/speckle
|
||||||
|
|
||||||
# Using poetry, we generate a list of requirements, save them to requirements.txt, and then use pip to install them
|
|
||||||
RUN poetry export --format requirements.txt --output /home/speckle/requirements.txt && pip install --requirement /home/speckle/requirements.txt
|
|
||||||
@@ -9,19 +9,49 @@ It also has some sane defaults for development environment setups.
|
|||||||
## Getting started
|
## Getting started
|
||||||
|
|
||||||
1. Use this template repository to create a new repository in your own / organization's profile.
|
1. Use this template repository to create a new repository in your own / organization's profile.
|
||||||
|
1. Register the function
|
||||||
Register the function
|
|
||||||
|
|
||||||
### Add new dependencies
|
### Add new dependencies
|
||||||
|
|
||||||
To add new Python package dependencies to the project, use the following:
|
To add new Python package dependencies to the project, edit the `pyproject.toml` file:
|
||||||
`$ poetry add pandas`
|
|
||||||
|
**For packages your function needs to run** (like pandas, requests, etc.):
|
||||||
|
```toml
|
||||||
|
dependencies = [
|
||||||
|
"specklepy==3.0.0",
|
||||||
|
"pandas==2.1.0", # Add production dependencies here
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
**For development tools** (like testing or formatting tools):
|
||||||
|
```toml
|
||||||
|
[project.optional-dependencies]
|
||||||
|
dev = [
|
||||||
|
"black==23.12.1",
|
||||||
|
"pytest-mock==3.11.1", # Add development dependencies here
|
||||||
|
# ... other dev tools
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
**How to decide which section?**
|
||||||
|
- If your `main.py` (or other function logic) imports it → `dependencies`
|
||||||
|
- If it's just a tool to help you code → `[project.optional-dependencies].dev`
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```python
|
||||||
|
# In your main.py
|
||||||
|
import pandas as pd # ← This goes in dependencies
|
||||||
|
import specklepy # ← This goes in dependencies
|
||||||
|
|
||||||
|
# You won't import these in main.py:
|
||||||
|
# pytest, black, mypy ← These go in [project.optional-dependencies].dev
|
||||||
|
```
|
||||||
|
|
||||||
### Change launch variables
|
### Change launch variables
|
||||||
|
|
||||||
Describe how the launch.json should be edited.
|
Describe how the launch.json should be edited.
|
||||||
|
|
||||||
### Github Codespaces
|
### GitHub Codespaces
|
||||||
|
|
||||||
Create a new repo from this template, and use the create new code.
|
Create a new repo from this template, and use the create new code.
|
||||||
|
|
||||||
@@ -43,13 +73,57 @@ Create a new repo from this template, and use the create new code.
|
|||||||
## Developer Requirements
|
## Developer Requirements
|
||||||
|
|
||||||
1. Install the following:
|
1. Install the following:
|
||||||
- [Python 3](https://www.python.org/downloads/)
|
- [Python 3.11+](https://www.python.org/downloads/)
|
||||||
- [Poetry](https://python-poetry.org/docs/#installing-with-the-official-installer)
|
1. Run the following to set up your development environment:
|
||||||
1. Run `poetry shell && poetry install` to install the required Python packages.
|
```bash
|
||||||
|
python -m venv .venv
|
||||||
|
# On Windows
|
||||||
|
.venv\Scripts\activate
|
||||||
|
# On macOS/Linux
|
||||||
|
source .venv/bin/activate
|
||||||
|
|
||||||
|
pip install --upgrade pip
|
||||||
|
pip install .[dev]
|
||||||
|
```
|
||||||
|
|
||||||
|
**What this installs:**
|
||||||
|
- All the packages your function needs to run (`dependencies`)
|
||||||
|
- Plus development tools like testing and code formatting (`[project.optional-dependencies].dev`)
|
||||||
|
|
||||||
|
**Why separate sections?**
|
||||||
|
- `dependencies`: Only what gets deployed with your function (lightweight)
|
||||||
|
- `dev` dependencies: Extra tools to help you write better code locally
|
||||||
|
|
||||||
## Building and Testing
|
## Building and Testing
|
||||||
|
|
||||||
The code can be tested locally by running `poetry run pytest`.
|
The code can be tested locally by running `pytest`.
|
||||||
|
|
||||||
|
### Alternative dependency managers
|
||||||
|
|
||||||
|
This template uses the modern **PEP 621** standard in `pyproject.toml`, which works with all modern Python dependency managers:
|
||||||
|
|
||||||
|
#### Using Poetry
|
||||||
|
```bash
|
||||||
|
poetry install # Automatically reads pyproject.toml
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Using uv
|
||||||
|
```bash
|
||||||
|
uv sync # Automatically reads pyproject.toml
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Using pip-tools
|
||||||
|
```bash
|
||||||
|
pip-compile pyproject.toml # Generate requirements.txt from pyproject.toml
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Using pdm
|
||||||
|
```bash
|
||||||
|
pdm install # Automatically reads pyproject.toml
|
||||||
|
```
|
||||||
|
|
||||||
|
**Advantage**: All tools read the same `pyproject.toml` file, so there's no need to keep multiple files in sync!
|
||||||
|
|
||||||
### Building and running the Docker Container Image
|
### Building and running the Docker Container Image
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -7,8 +7,8 @@ from specklepy.objects import Base
|
|||||||
|
|
||||||
def flatten_base(base: Base) -> Iterable[Base]:
|
def flatten_base(base: Base) -> Iterable[Base]:
|
||||||
"""Flatten a base object into an iterable of bases.
|
"""Flatten a base object into an iterable of bases.
|
||||||
|
|
||||||
This function recursively traverses the `elements` or `@elements` attribute of the
|
This function recursively traverses the `elements` or `@elements` attribute of the
|
||||||
base object, yielding each nested base object.
|
base object, yielding each nested base object.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -19,9 +19,9 @@ def flatten_base(base: Base) -> Iterable[Base]:
|
|||||||
"""
|
"""
|
||||||
# Attempt to get the elements attribute, fallback to @elements if necessary
|
# Attempt to get the elements attribute, fallback to @elements if necessary
|
||||||
elements = getattr(base, "elements", getattr(base, "@elements", None))
|
elements = getattr(base, "elements", getattr(base, "@elements", None))
|
||||||
|
|
||||||
if elements is not None:
|
if elements is not None:
|
||||||
for element in elements:
|
for element in elements:
|
||||||
yield from flatten_base(element)
|
yield from flatten_base(element)
|
||||||
|
|
||||||
yield base
|
yield base
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ def automate_function(
|
|||||||
automate_context: A context-helper object that carries relevant information
|
automate_context: A context-helper object that carries relevant information
|
||||||
about the runtime context of this function.
|
about the runtime context of this function.
|
||||||
It gives access to the Speckle project data that triggered this run.
|
It gives access to the Speckle project data that triggered this run.
|
||||||
It also has convenient methods for attaching result data to the Speckle model.
|
It also has convenient methods for attaching results to the Speckle model.
|
||||||
function_inputs: An instance object matching the defined schema.
|
function_inputs: An instance object matching the defined schema.
|
||||||
"""
|
"""
|
||||||
# The context provides a convenient way to receive the triggering version.
|
# The context provides a convenient way to receive the triggering version.
|
||||||
@@ -60,7 +60,7 @@ def automate_function(
|
|||||||
automate_context.attach_error_to_objects(
|
automate_context.attach_error_to_objects(
|
||||||
category="Forbidden speckle_type"
|
category="Forbidden speckle_type"
|
||||||
f" ({function_inputs.forbidden_speckle_type})",
|
f" ({function_inputs.forbidden_speckle_type})",
|
||||||
object_ids=[o.id for o in objects_with_forbidden_speckle_type if o.id],
|
affected_objects=objects_with_forbidden_speckle_type,
|
||||||
message="This project should not contain the type: "
|
message="This project should not contain the type: "
|
||||||
f"{function_inputs.forbidden_speckle_type}",
|
f"{function_inputs.forbidden_speckle_type}",
|
||||||
)
|
)
|
||||||
|
|||||||
Generated
-1421
File diff suppressed because it is too large
Load Diff
+22
-20
@@ -1,28 +1,27 @@
|
|||||||
[tool.poetry]
|
[project]
|
||||||
name = "speckle-automate-py"
|
name = "speckle-automate-function"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
description = "Example function for Speckle Automate using specklepy"
|
requires-python = ">=3.11"
|
||||||
authors = ["Gergő Jedlicska <gergo@jedlicska.com>"]
|
authors = [{ name = "Speckle Systems", email = "hello@speckle.systems" }]
|
||||||
|
maintainers = [{ name = "Speckle Systems", email = "hello@speckle.systems" }]
|
||||||
|
description = "A Speckle Automate function template using specklepy"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
package-mode = false
|
license = "Apache-2.0"
|
||||||
|
keywords = ["speckle", "automate", "bim", "aec"]
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
dependencies = ["specklepy==3.0.0"]
|
||||||
python = "^3.11"
|
|
||||||
specklepy = "^2.21.4"
|
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[project.optional-dependencies]
|
||||||
black = "^23.3.0"
|
dev = [
|
||||||
mypy = "^1.3.0"
|
"mypy==1.13.0",
|
||||||
ruff = "^0.0.271"
|
"pytest==7.4.4",
|
||||||
pydantic-settings = "^2.3.0"
|
"ruff==0.11.12",
|
||||||
pytest = "^7.4.2"
|
]
|
||||||
# specklepy = { path = "../specklepy", develop = true }
|
|
||||||
|
|
||||||
[build-system]
|
|
||||||
requires = ["poetry-core"]
|
|
||||||
build-backend = "poetry.core.masonry.api"
|
|
||||||
|
|
||||||
[tool.ruff]
|
[tool.ruff]
|
||||||
|
exclude = [".venv", "**/*.yml"]
|
||||||
|
|
||||||
|
[tool.ruff.lint]
|
||||||
select = [
|
select = [
|
||||||
"E", # pycodestyle
|
"E", # pycodestyle
|
||||||
"F", # pyflakes
|
"F", # pyflakes
|
||||||
@@ -31,5 +30,8 @@ select = [
|
|||||||
"I", # isort
|
"I", # isort
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.ruff.pydocstyle]
|
[tool.ruff.lint.pydocstyle]
|
||||||
convention = "google"
|
convention = "google"
|
||||||
|
|
||||||
|
[tool.setuptools]
|
||||||
|
py-modules = []
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
"""Tests for the automate function."""
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
"""Run integration tests with a speckle server."""
|
"""Run integration tests with a speckle server."""
|
||||||
|
|
||||||
from pydantic import SecretStr
|
from pydantic import SecretStr
|
||||||
|
|
||||||
from speckle_automate import (
|
from speckle_automate import (
|
||||||
AutomationContext,
|
AutomationContext,
|
||||||
AutomationRunData,
|
AutomationRunData,
|
||||||
AutomationStatus,
|
AutomationStatus,
|
||||||
run_function
|
run_function,
|
||||||
)
|
)
|
||||||
|
from speckle_automate.fixtures import * # noqa: F403
|
||||||
|
|
||||||
from main import FunctionInputs, automate_function
|
from main import FunctionInputs, automate_function
|
||||||
|
|
||||||
from speckle_automate.fixtures import *
|
|
||||||
|
|
||||||
|
def test_function_run(
|
||||||
def test_function_run(test_automation_run_data: AutomationRunData, test_automation_token: str):
|
test_automation_run_data: AutomationRunData, test_automation_token: str
|
||||||
|
):
|
||||||
"""Run an integration test for the automate function."""
|
"""Run an integration test for the automate function."""
|
||||||
automation_context = AutomationContext.initialize(
|
automation_context = AutomationContext.initialize(
|
||||||
test_automation_run_data, test_automation_token
|
test_automation_run_data, test_automation_token
|
||||||
|
|||||||
Reference in New Issue
Block a user