Compare commits

..

6 Commits

Author SHA1 Message Date
Dogukan Karatas c04a97780c update from main 2025-11-11 08:56:16 +01:00
Dogukan Karatas c5cd69569e docs poc 2025-03-05 12:50:44 +01:00
Jedd Morgan e38249bc38 Moved docs one layer higher 2025-02-28 16:37:54 +00:00
Jedd Morgan 08fbf59c8a experiments 2025-02-28 16:21:43 +00:00
Jedd Morgan e9cdd3e900 round2 2025-02-28 15:34:59 +00:00
Jedd Morgan 20bb0449e8 first pass 2025-02-28 15:10:44 +00:00
135 changed files with 1586 additions and 4311 deletions
-6
View File
@@ -13,14 +13,12 @@ jobs:
name: Test (internal) name: Test (internal)
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
fail-fast: false
matrix: matrix:
python-version: python-version:
- "3.10" - "3.10"
- "3.11" - "3.11"
- "3.12" - "3.12"
- "3.13" - "3.13"
- "3.14"
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -61,17 +59,13 @@ jobs:
test-public: # Run integration tests against the public server image test-public: # Run integration tests against the public server image
name: Test (public) name: Test (public)
runs-on: ubuntu-latest runs-on: ubuntu-latest
env:
IS_PUBLIC: "true"
strategy: strategy:
fail-fast: false
matrix: matrix:
python-version: python-version:
- "3.10" - "3.10"
- "3.11" - "3.11"
- "3.12" - "3.12"
- "3.13" - "3.13"
- "3.14"
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
-1
View File
@@ -1 +0,0 @@
words = ["specklepy"]
+4 -1
View File
@@ -97,7 +97,10 @@ services:
STRATEGY_LOCAL: "true" STRATEGY_LOCAL: "true"
POSTGRES_URL: 'postgres://speckle:speckle@postgres:5432/speckle' POSTGRES_URL: "postgres"
POSTGRES_USER: "speckle"
POSTGRES_PASSWORD: "speckle"
POSTGRES_DB: "speckle"
ENABLE_MP: "false" ENABLE_MP: "false"
LOG_PRETTY: "true" LOG_PRETTY: "true"
-50
View File
@@ -1,50 +0,0 @@
# specklepy API Reference
> The Python SDK for Speckle - Build powerful AEC data workflows
**specklepy** is the Python SDK for Speckle, enabling you to interact with Speckle Server, send and receive geometry, and build custom integrations for the AEC industry.
## What is specklepy?
specklepy is a comprehensive Python library that provides:
* **Object-based data exchange** - Send and receive geometry and BIM data without files
* **GraphQL API client** - Full access to Speckle Server's API
* **Extensible object model** - Create custom objects that inherit from `Base`
* **Multiple transport options** - Store data locally (SQLite), in-memory, or on Speckle Server
* **Geometry support** - Rich geometric primitives (Point, Line, Mesh, etc.)
## Speckle Automate
Speckle Automate is a fully fledged CI/CD platform designed to run custom code on Speckle models whenever a new version is available.
As a software developer, you can develop Functions that others in your team consume in Automations. From creating reports to running code compliance checks to wind simulations, there is no limit to what you can do with Automate.
## Installation
Install specklepy using pip:
```bash
pip install specklepy
```
## Quick Example
```python
from specklepy.api.client import SpeckleClient
from specklepy.api.credentials import get_default_account
from specklepy.objects.geometry import Point
# Authenticate
client = SpeckleClient(host="https://app.speckle.systems")
account = get_default_account()
client.authenticate_with_account(account)
# Create geometry
point = Point(x=10, y=20, z=5)
```
## Getting Help
- **Community Forum**: [speckle.community](https://speckle.community/c/help/developers)
- **GitHub Issues**: [github.com/specklesystems/specklepy](https://github.com/specklesystems/specklepy/issues)
@@ -1 +0,0 @@
::: speckle_automate.AutomationContext
-3
View File
@@ -1,3 +0,0 @@
::: speckle_automate.runner.execute_automate_function
::: speckle_automate.runner.run_function
-11
View File
@@ -1,11 +0,0 @@
::: speckle_automate.AutomateBase
::: speckle_automate.AutomationRunData
::: speckle_automate.AutomationResult
::: speckle_automate.ResultCase
::: speckle_automate.AutomationStatus
::: speckle_automate.ObjectResultLevel
-5
View File
@@ -1,5 +0,0 @@
::: specklepy.api.credentials.Account
::: specklepy.api.credentials.UserInfo
::: specklepy.api.credentials.StreamWrapper
-7
View File
@@ -1,7 +0,0 @@
::: specklepy.api.operations.send
::: specklepy.api.operations.receive
::: specklepy.api.operations.serialize
::: specklepy.api.operations.deserialize
@@ -1 +0,0 @@
::: specklepy.api.resources.ActiveUserResource
@@ -1 +0,0 @@
::: specklepy.api.resources.FileImportResource
@@ -1 +0,0 @@
::: specklepy.api.resources.ModelResource
@@ -1 +0,0 @@
::: specklepy.api.resources.OtherUserResource
@@ -1 +0,0 @@
::: specklepy.api.resources.ProjectInviteResource
@@ -1 +0,0 @@
::: specklepy.api.resources.ProjectResource
@@ -1 +0,0 @@
::: specklepy.api.resources.ServerResource
@@ -1 +0,0 @@
::: specklepy.api.resources.SubscriptionResource
@@ -1 +0,0 @@
::: specklepy.api.resources.VersionResource
@@ -1 +0,0 @@
::: specklepy.api.resources.WorkspaceResource
-1
View File
@@ -1 +0,0 @@
::: specklepy.core.api.enums.ProjectVisibility
-11
View File
@@ -1,11 +0,0 @@
::: specklepy.core.api.inputs.ProjectCreateInput
::: specklepy.core.api.inputs.ProjectUpdateInput
::: specklepy.core.api.inputs.CreateModelInput
::: specklepy.core.api.inputs.UpdateModelInput
::: specklepy.core.api.inputs.CreateVersionInput
::: specklepy.core.api.inputs.UpdateVersionInput
-13
View File
@@ -1,13 +0,0 @@
::: specklepy.core.api.models.User
::: specklepy.core.api.models.LimitedUser
::: specklepy.core.api.models.ServerInfo
::: specklepy.core.api.models.Project
::: specklepy.core.api.models.Model
::: specklepy.core.api.models.Version
::: specklepy.core.api.models.current.Workspace
-7
View File
@@ -1,7 +0,0 @@
::: specklepy.logging.exceptions.SpeckleException
::: specklepy.logging.exceptions.GraphQLException
::: specklepy.logging.exceptions.SerializationException
::: specklepy.logging.exceptions.SpeckleWarning
-3
View File
@@ -1,3 +0,0 @@
::: specklepy.objects.Base
::: specklepy.objects.base.DataChunk
-5
View File
@@ -1,5 +0,0 @@
::: specklepy.objects.DataObject
::: specklepy.objects.QgisObject
::: specklepy.objects.BlenderObject
-1
View File
@@ -1 +0,0 @@
::: specklepy.objects.geometry.Arc
-1
View File
@@ -1 +0,0 @@
::: specklepy.objects.geometry.Box
@@ -1 +0,0 @@
::: specklepy.objects.geometry.Circle
@@ -1 +0,0 @@
::: specklepy.objects.geometry.ControlPoint
-1
View File
@@ -1 +0,0 @@
::: specklepy.objects.geometry.Curve
@@ -1 +0,0 @@
::: specklepy.objects.geometry.Ellipse
-1
View File
@@ -1 +0,0 @@
::: specklepy.objects.geometry.Line
-1
View File
@@ -1 +0,0 @@
::: specklepy.objects.geometry.Mesh
-1
View File
@@ -1 +0,0 @@
::: specklepy.objects.geometry.Plane
-1
View File
@@ -1 +0,0 @@
::: specklepy.objects.geometry.Point
@@ -1 +0,0 @@
::: specklepy.objects.geometry.PointCloud
@@ -1 +0,0 @@
::: specklepy.objects.geometry.Polycurve
@@ -1 +0,0 @@
::: specklepy.objects.geometry.Polyline
@@ -1 +0,0 @@
::: specklepy.objects.geometry.Spiral
@@ -1 +0,0 @@
::: specklepy.objects.geometry.Surface
@@ -1 +0,0 @@
::: specklepy.objects.geometry.Vector
@@ -1,7 +0,0 @@
::: specklepy.objects.graph_traversal.GraphTraversal
::: specklepy.objects.graph_traversal.TraversalContext
::: specklepy.objects.graph_traversal.TraversalRule
::: specklepy.objects.graph_traversal.DefaultRule
@@ -1 +0,0 @@
::: specklepy.objects.models.collections.collection.Collection
@@ -1 +0,0 @@
::: specklepy.objects.proxies.LevelProxy
@@ -1 +0,0 @@
::: specklepy.serialization.base_object_serializer.BaseObjectSerializer
-7
View File
@@ -1,7 +0,0 @@
::: specklepy.transports.abstract_transport.AbstractTransport
::: specklepy.transports.memory.MemoryTransport
::: specklepy.transports.sqlite.SQLiteTransport
::: specklepy.transports.server.ServerTransport
-304
View File
@@ -1,304 +0,0 @@
.md-content h1 {
font-size: 1.125rem;
font-weight: 700;
color: #111827;
letter-spacing: -0.025em;
line-height: 1.2;
}
@media (min-width: 640px) {
.md-content h1 {
font-size: 1.25rem;
}
}
[data-md-color-scheme="slate"] .md-content h1 {
color: #e5e7eb;
}
.md-content h2 {
font-size: 1rem;
font-weight: 700;
color: #111827;
letter-spacing: -0.025em;
margin-top: 2rem;
}
@media (min-width: 640px) {
.md-content h2 {
font-size: 1.125rem;
}
}
[data-md-color-scheme="slate"] .md-content h2 {
color: #e5e7eb;
}
.md-content h3 {
font-size: 0.9375rem;
font-weight: 600;
color: #1f2937;
margin-top: 1.5rem;
}
@media (min-width: 640px) {
.md-content h3 {
font-size: 1rem;
}
}
[data-md-color-scheme="slate"] .md-content h3 {
color: #d1d5db;
}
.md-content p,
.md-content li {
font-size: 0.875rem;
line-height: 1.6;
color: #374151;
margin-top: 0.5rem;
}
[data-md-color-scheme="slate"] .md-content p,
[data-md-color-scheme="slate"] .md-content li {
color: #d1d5db;
}
.doc.doc-object-name {
font-size: 1.125rem;
font-weight: 700;
color: #111827;
}
[data-md-color-scheme="slate"] .doc.doc-object-name {
color: #e5e7eb;
}
.doc.doc-heading {
font-size: 0.9375rem;
font-weight: 600;
color: #1f2937;
}
[data-md-color-scheme="slate"] .doc.doc-heading {
color: #d1d5db;
}
.md-content code {
font-size: 0.75rem;
color: #dc2626;
background-color: #f3f4f6;
padding: 0.125rem 0.375rem;
border-radius: 0.25rem;
}
[data-md-color-scheme="slate"] .md-content code {
color: #fca5a5;
background-color: #1f2937;
}
.md-content pre code {
font-size: 0.75rem;
color: inherit;
background-color: transparent;
padding: 0;
}
.md-content a {
color: #2563eb;
text-decoration: none;
}
.md-content a:hover {
color: #1d4ed8;
text-decoration: underline;
}
[data-md-color-scheme="slate"] .md-content a {
color: #60a5fa;
}
[data-md-color-scheme="slate"] .md-content a:hover {
color: #93c5fd;
}
[data-md-color-scheme="slate"] {
--md-default-bg-color: #000000;
--md-default-fg-color: #ffffff;
--md-code-bg-color: #0a0a0a;
--md-code-fg-color: #ffffff;
}
[data-md-color-scheme="slate"] .md-header {
background-color: #000000;
}
[data-md-color-scheme="slate"] .md-tabs {
background-color: #000000;
}
[data-md-color-scheme="slate"] .md-footer {
background-color: #000000;
}
[data-md-color-scheme="slate"] .md-sidebar {
background-color: #000000;
}
[data-md-color-scheme="slate"] .md-nav {
background-color: #000000;
}
.highlight pre,
.highlight code {
background-color: #f6f8fa !important;
color: #24292e !important;
}
.highlight .k,
.highlight .kc,
.highlight .kd,
.highlight .kn,
.highlight .kp,
.highlight .kr,
.highlight .kt {
color: #d73a49 !important;
}
.highlight .s,
.highlight .s1,
.highlight .s2,
.highlight .sb,
.highlight .sc,
.highlight .sd,
.highlight .se,
.highlight .sh,
.highlight .si,
.highlight .sx,
.highlight .sr,
.highlight .ss {
color: #032f62 !important;
}
.highlight .nf,
.highlight .fm,
.highlight .nc {
color: #6f42c1 !important;
}
.highlight .m,
.highlight .mf,
.highlight .mh,
.highlight .mi,
.highlight .mo {
color: #005cc5 !important;
}
.highlight .c,
.highlight .c1,
.highlight .cm,
.highlight .cp,
.highlight .cs {
color: #6a737d !important;
font-style: italic;
}
.highlight .o,
.highlight .ow {
color: #d73a49 !important;
}
.highlight .n,
.highlight .na,
.highlight .nb,
.highlight .nd,
.highlight .ni,
.highlight .nl,
.highlight .nn,
.highlight .nx,
.highlight .py,
.highlight .nt,
.highlight .nv,
.highlight .bp,
.highlight .vc,
.highlight .vg,
.highlight .vi {
color: #e36209 !important;
}
[data-md-color-scheme="slate"] .highlight pre,
[data-md-color-scheme="slate"] .highlight code {
background-color: #0d1117 !important;
color: #e6edf3 !important;
}
[data-md-color-scheme="slate"] .highlight .k,
[data-md-color-scheme="slate"] .highlight .kc,
[data-md-color-scheme="slate"] .highlight .kd,
[data-md-color-scheme="slate"] .highlight .kn,
[data-md-color-scheme="slate"] .highlight .kp,
[data-md-color-scheme="slate"] .highlight .kr,
[data-md-color-scheme="slate"] .highlight .kt {
color: #ff7b72 !important;
}
[data-md-color-scheme="slate"] .highlight .s,
[data-md-color-scheme="slate"] .highlight .s1,
[data-md-color-scheme="slate"] .highlight .s2,
[data-md-color-scheme="slate"] .highlight .sb,
[data-md-color-scheme="slate"] .highlight .sc,
[data-md-color-scheme="slate"] .highlight .sd,
[data-md-color-scheme="slate"] .highlight .se,
[data-md-color-scheme="slate"] .highlight .sh,
[data-md-color-scheme="slate"] .highlight .si,
[data-md-color-scheme="slate"] .highlight .sx,
[data-md-color-scheme="slate"] .highlight .sr,
[data-md-color-scheme="slate"] .highlight .ss {
color: #a5d6ff !important;
}
[data-md-color-scheme="slate"] .highlight .nf,
[data-md-color-scheme="slate"] .highlight .fm,
[data-md-color-scheme="slate"] .highlight .nc {
color: #d2a8ff !important;
}
[data-md-color-scheme="slate"] .highlight .m,
[data-md-color-scheme="slate"] .highlight .mf,
[data-md-color-scheme="slate"] .highlight .mh,
[data-md-color-scheme="slate"] .highlight .mi,
[data-md-color-scheme="slate"] .highlight .mo {
color: #79c0ff !important;
}
[data-md-color-scheme="slate"] .highlight .c,
[data-md-color-scheme="slate"] .highlight .c1,
[data-md-color-scheme="slate"] .highlight .cm,
[data-md-color-scheme="slate"] .highlight .cp,
[data-md-color-scheme="slate"] .highlight .cs {
color: #8b949e !important;
font-style: italic;
}
[data-md-color-scheme="slate"] .highlight .o,
[data-md-color-scheme="slate"] .highlight .ow {
color: #ff7b72 !important;
}
[data-md-color-scheme="slate"] .highlight .n,
[data-md-color-scheme="slate"] .highlight .na,
[data-md-color-scheme="slate"] .highlight .nb,
[data-md-color-scheme="slate"] .highlight .nd,
[data-md-color-scheme="slate"] .highlight .ni,
[data-md-color-scheme="slate"] .highlight .nl,
[data-md-color-scheme="slate"] .highlight .nn,
[data-md-color-scheme="slate"] .highlight .nx,
[data-md-color-scheme="slate"] .highlight .py,
[data-md-color-scheme="slate"] .highlight .nt,
[data-md-color-scheme="slate"] .highlight .nv,
[data-md-color-scheme="slate"] .highlight .bp,
[data-md-color-scheme="slate"] .highlight .vc,
[data-md-color-scheme="slate"] .highlight .vg,
[data-md-color-scheme="slate"] .highlight .vi {
color: #ffa657 !important;
}
-21
View File
@@ -1,27 +1,6 @@
[tools] [tools]
python = "3.13.7" python = "3.13.7"
uv = "0.9.11"
[settings] [settings]
experimental = true experimental = true
python.uv_venv_auto = true python.uv_venv_auto = true
[tasks.install]
run= "uv sync --all-extras --all-groups"
[tasks.install_docs]
run= "uv sync --group docs"
[tasks.build_docs]
description = "Build static docs "
run = "uv run mkdocs build"
depends = ['install_docs']
[tasks.test]
run = "uv run pytest"
[env]
IS_PUBLIC = "false"
-130
View File
@@ -1,130 +0,0 @@
site_name: specklepy Docs
theme:
name: material
font:
text: Inter
favicon: assets/speckle_logo.png
logo: assets/speckle_logo.png
features:
- navigation.tabs
palette:
# Palette toggle for light mode
- scheme: default
primary: white
toggle:
icon: material/weather-night
name: Switch to dark mode
# Palette toggle for dark mode
- scheme: slate
primary: black
logo: assets/logo_white.png
toggle:
icon: material/weather-sunny
name: Switch to light mode
markdown_extensions:
- pymdownx.highlight:
anchor_linenums: true
line_spans: __span
pygments_lang_class: true
- pymdownx.inlinehilite
- pymdownx.snippets
- pymdownx.superfences
extra_css:
- stylesheets/extra.css
plugins:
- search
- mkdocstrings:
handlers:
python:
paths: [src]
options:
parameter_headings: false
members_order: source
separate_signature: true
filters: ["!^_"] #Ignore _ prefixed properties
docstring_options:
ignore_init_summary: true
merge_init_into_class: true
show_signature_annotations: true
signature_crossrefs: true
show_if_no_docstring: true
show_labels: true
show_source: true
show_symbol_type_heading: true
show_symbol_type_toc: true
show_bases: false
heading_level: 3
inventories:
- url: https://docs.python.org/3/objects.inv
domains: [py, std]
nav:
- Home: index.md
- specklepy SDK:
- API:
- Client: specklepy/api/client.md
- Credentials: specklepy/api/credentials.md
- Operations: specklepy/api/operations.md
- Resources:
- ActiveUserResource: specklepy/api/resources/ActiveUserResource.md
- FileImportResource: specklepy/api/resources/FileImportResource.md
- ModelResource: specklepy/api/resources/ModelResource.md
- OtherUserResource: specklepy/api/resources/OtherUserResource.md
- ProjectInviteResource: specklepy/api/resources/ProjectInviteResource.md
- ProjectResource: specklepy/api/resources/ProjectResource.md
- ServerResource: specklepy/api/resources/ServerResource.md
- SubscriptionResource: specklepy/api/resources/SubscriptionResource.md
- VersionResource: specklepy/api/resources/VersionResource.md
- WorkspaceResource: specklepy/api/resources/WorkspaceResource.md
- Objects:
- Base: specklepy/objects/base.md
- Data Objects: specklepy/objects/data_objects.md
- Geometry:
- Arc: specklepy/objects/geometry/Arc.md
- Box: specklepy/objects/geometry/Box.md
- Circle: specklepy/objects/geometry/Circle.md
- ControlPoint: specklepy/objects/geometry/ControlPoint.md
- Curve: specklepy/objects/geometry/Curve.md
- Ellipse: specklepy/objects/geometry/Ellipse.md
- Line: specklepy/objects/geometry/Line.md
- Mesh: specklepy/objects/geometry/Mesh.md
- Plane: specklepy/objects/geometry/Plane.md
- Point: specklepy/objects/geometry/Point.md
- PointCloud: specklepy/objects/geometry/PointCloud.md
- Polycurve: specklepy/objects/geometry/Polycurve.md
- Polyline: specklepy/objects/geometry/Polyline.md
- Spiral: specklepy/objects/geometry/Spiral.md
- Surface: specklepy/objects/geometry/Surface.md
- Vector: specklepy/objects/geometry/Vector.md
- Primitives:
- Interval: specklepy/objects/primitives/interval.md
- Other:
- RenderMaterial: specklepy/objects/other/render_material.md
- Collection: specklepy/objects/other/collection.md
- Proxies:
- ColorProxy: specklepy/objects/proxies/ColorProxy.md
- GroupProxy: specklepy/objects/proxies/GroupProxy.md
- InstanceProxy: specklepy/objects/proxies/InstanceProxy.md
- InstanceDefinitionProxy: specklepy/objects/proxies/InstanceDefinitionProxy.md
- LevelProxy: specklepy/objects/proxies/LevelProxy.md
- RenderMaterialProxy: specklepy/objects/proxies/RenderMaterialProxy.md
- Graph Traversal: specklepy/objects/graph_traversal/traversal.md
- Transports: specklepy/transports/transports.md
- Serialization: specklepy/serialization/serializer.md
- Core API:
- Models: specklepy/core/api/models/models.md
- Inputs: specklepy/core/api/inputs/inputs.md
- Enums: specklepy/core/api/enums.md
- Logging:
- Exceptions: specklepy/logging/exceptions.md
- Speckle Automate:
- AutomationContext: speckle_automate/automation_context.md
- Runner: speckle_automate/runner.md
- Schema: speckle_automate/schema.md
-3
View File
@@ -1,3 +0,0 @@
[build]
command = "mise run build_docs"
publish = "site"
+5 -9
View File
@@ -13,6 +13,10 @@ dependencies = [
"deprecated>=1.2.15", "deprecated>=1.2.15",
"gql[requests,websockets]>=3.5.0,<4.0.0", "gql[requests,websockets]>=3.5.0,<4.0.0",
"httpx>=0.28.1", "httpx>=0.28.1",
"mkdocs>=1.6.1",
"mkdocs-material>=9.6.5",
"mkdocstrings>=0.28.1",
"mkdocstrings-python>=1.15.0",
"pydantic>=2.10.5", "pydantic>=2.10.5",
"pydantic-settings>=2.7.1", "pydantic-settings>=2.7.1",
"ujson>=5.10.0", "ujson>=5.10.0",
@@ -22,7 +26,6 @@ dependencies = [
speckleifc = ["ifcopenshell>=0.8.3.post2"] speckleifc = ["ifcopenshell>=0.8.3.post2"]
[dependency-groups] [dependency-groups]
dev = [ dev = [
"commitizen>=4.1.0", "commitizen>=4.1.0",
"devtools>=0.12.2", "devtools>=0.12.2",
@@ -33,18 +36,11 @@ dev = [
"pytest-asyncio>=0.25.2", "pytest-asyncio>=0.25.2",
"pytest-cov>=6.0.0", "pytest-cov>=6.0.0",
"pytest-ordering>=0.6", "pytest-ordering>=0.6",
"pytest_httpserver >=1.1.3", "ruff>=0.9.2",
"ruff==0.9.2",
"types-deprecated>=1.2.15.20241117", "types-deprecated>=1.2.15.20241117",
"types-requests>=2.32.0.20241016", "types-requests>=2.32.0.20241016",
"types-ujson>=5.10.0.20240515", "types-ujson>=5.10.0.20240515",
] ]
docs = [
"mkdocs>=1.6.1",
"mkdocs-material>=9.6.5",
"mkdocstrings>=0.28.1",
"mkdocstrings-python>=1.15.0",
]
[project.urls] [project.urls]
repository = "https://github.com/specklesystems/specklepy" repository = "https://github.com/specklesystems/specklepy"
@@ -0,0 +1 @@
::: specklepy.objects.data_objects.DataObject
@@ -0,0 +1 @@
::: specklepy.objects.data_objects.QgisObject
@@ -0,0 +1 @@
::: specklepy.objects.interfaces.IBlenderObject
@@ -0,0 +1 @@
::: specklepy.objects.interfaces.ICurve
@@ -0,0 +1 @@
::: specklepy.objects.interfaces.IDataObject
@@ -0,0 +1 @@
::: specklepy.objects.interfaces.IDisplayValue
@@ -0,0 +1 @@
::: specklepy.objects.interfaces.IBlenderObject
@@ -0,0 +1 @@
::: specklepy.objects.interfaces.IHasArea
@@ -0,0 +1 @@
::: specklepy.objects.interfaces.IHasUnits
@@ -0,0 +1 @@
::: specklepy.objects.interfaces.IHasVolume
@@ -0,0 +1 @@
::: specklepy.objects.interfaces.IProperties
+1
View File
@@ -0,0 +1 @@
::: specklepy.objects.base.Base
@@ -0,0 +1 @@
::: specklepy.objects.geometry.arc.Arc
@@ -0,0 +1 @@
::: specklepy.objects.geometry.box.Box
@@ -0,0 +1 @@
::: specklepy.objects.geometry.circle.Circle
@@ -0,0 +1 @@
::: specklepy.objects.geometry.control_point.ControlPoint
@@ -0,0 +1 @@
::: specklepy.objects.geometry.ellipse.Ellipse
@@ -0,0 +1 @@
::: specklepy.objects.geometry.line.Line
@@ -0,0 +1 @@
::: specklepy.objects.geometry.mesh.Mesh
@@ -0,0 +1 @@
::: specklepy.objects.geometry.plane.Plane
@@ -0,0 +1 @@
::: specklepy.objects.geometry.point.Point
@@ -0,0 +1 @@
::: specklepy.objects.geometry.point_cloud.PointCloud
@@ -0,0 +1 @@
::: specklepy.objects.geometry.polycurve.Polycurve
@@ -0,0 +1 @@
::: specklepy.objects.geometry.polyline.Polyline
@@ -0,0 +1 @@
::: specklepy.objects.geometry.spiral.Spiral
@@ -0,0 +1 @@
::: specklepy.objects.geometry.surface.Surface
@@ -0,0 +1 @@
::: specklepy.objects.geometry.vector.Vector

Before

Width:  |  Height:  |  Size: 386 B

After

Width:  |  Height:  |  Size: 386 B

+29
View File
@@ -0,0 +1,29 @@
# Introduction
Welcome to the Specklepy Developer Docs - a single source of documentation on everything Specklepy! If you're looking for info on how to use Speckle, check our [user guide](https://speckle.guide/).
### Code Repository
The Python SDK can be found in our [repository](//github.com/specklesystems/specklepy), its readme contains instructions on how to build it.
### Installation
You can install it using pip
```
pip install specklepy
```
### Key Components
SpecklePy has three main parts:
1. a `SpeckleClient` which allows you to interact with the server API
2. `operations` and `transports` for sending and receiving large objects
3. a `Base` object and accompaniying serializer for creating and customizing your own Speckle objects
### Local Data Paths
It may be helpful to know where the local accounts and object cache dbs are stored. Depending on on your OS, you can find the dbs at:
- Windows: `APPDATA` or `<USER>\AppData\Roaming\Speckle`
- Linux: `$XDG_DATA_HOME` or by default `~/.local/share/Speckle`
- Mac: `~/.config/Speckle`
@@ -0,0 +1 @@
::: speckle_automate.automation_context.AutomationContext
+64
View File
@@ -0,0 +1,64 @@
site_name: Specklepy Docs
theme:
name: material
favicon: assets/speckle_logo.png
logo: assets/speckle_logo.png
features:
- navigation.tabs
palette:
# Palette toggle for light mode
- scheme: default
primary: white
toggle:
icon: material/weather-night
name: Switch to dark mode
# Palette toggle for dark mode
- scheme: slate
primary: black
logo: assets/logo_white.png
toggle:
icon: material/weather-sunny
name: Switch to light mode
markdown_extensions:
- pymdownx.highlight:
anchor_linenums: true
line_spans: __span
pygments_lang_class: true
- pymdownx.inlinehilite
- pymdownx.snippets
- pymdownx.superfences
extra_css:
- css/mkdocstrings.css
plugins:
- search
- mkdocstrings:
handlers:
python:
paths: [.]
options:
parameter_headings: false
members_order: source
separate_signature: true
filters: ["!^_"] #Ignore _ prefixed properties
docstring_options:
ignore_init_summary: true
merge_init_into_class: true
show_signature_annotations: true
signature_crossrefs: true
show_if_no_docstring: true
show_labels: true
show_source: true
show_symbol_type_heading: true
show_symbol_type_toc: true
show_bases: false
heading_level: 3
inventories:
- url: https://docs.python.org/3/objects.inv
domains: [py, std]
+11 -24
View File
@@ -19,12 +19,8 @@ from speckle_automate.schema import (
from specklepy.api import operations from specklepy.api import operations
from specklepy.api.client import SpeckleClient from specklepy.api.client import SpeckleClient
from specklepy.core.api.inputs.model_inputs import CreateModelInput from specklepy.core.api.inputs.model_inputs import CreateModelInput
from specklepy.core.api.inputs.version_inputs import ( from specklepy.core.api.inputs.version_inputs import CreateVersionInput
CreateVersionInput,
MarkReceivedVersionInput,
)
from specklepy.core.api.models.current import Model, Version from specklepy.core.api.models.current import Model, Version
from specklepy.logging import metrics
from specklepy.logging.exceptions import SpeckleException from specklepy.logging.exceptions import SpeckleException
from specklepy.objects.base import Base from specklepy.objects.base import Base
from specklepy.transports.memory import MemoryTransport from specklepy.transports.memory import MemoryTransport
@@ -70,7 +66,6 @@ class AutomationContext:
if isinstance(automation_run_data, AutomationRunData) if isinstance(automation_run_data, AutomationRunData)
else AutomationRunData.model_validate_json(automation_run_data) else AutomationRunData.model_validate_json(automation_run_data)
) )
metrics.set_host_app("automate")
speckle_client = SpeckleClient( speckle_client = SpeckleClient(
automation_run_data.speckle_server_url, automation_run_data.speckle_server_url,
automation_run_data.speckle_server_url.startswith("https"), automation_run_data.speckle_server_url.startswith("https"),
@@ -105,7 +100,6 @@ class AutomationContext:
"""Receive the Speckle project version that triggered this automation run.""" """Receive the Speckle project version that triggered this automation run."""
# TODO: this is a quick hack to keep implementation consistency. # TODO: this is a quick hack to keep implementation consistency.
# Move to proper receive many versions # Move to proper receive many versions
project_id = self.automation_run_data.project_id
version_id = self.automation_run_data.triggers[0].payload.version_id version_id = self.automation_run_data.triggers[0].payload.version_id
try: try:
version = self.speckle_client.version.get( version = self.speckle_client.version.get(
@@ -115,7 +109,7 @@ class AutomationContext:
raise ValueError( raise ValueError(
f"""Could not receive specified version. f"""Could not receive specified version.
Is your environment configured correctly? Is your environment configured correctly?
project_id: {project_id} project_id: {self.automation_run_data.project_id}
model_id: {self.automation_run_data.triggers[0].payload.model_id} model_id: {self.automation_run_data.triggers[0].payload.model_id}
version_id: {self.automation_run_data.triggers[0].payload.version_id} version_id: {self.automation_run_data.triggers[0].payload.version_id}
""" """
@@ -130,13 +124,6 @@ class AutomationContext:
base = operations.receive( base = operations.receive(
version.referenced_object, self._server_transport, self._memory_transport version.referenced_object, self._server_transport, self._memory_transport
) )
self.speckle_client.version.received(
MarkReceivedVersionInput(
version_id=version_id,
project_id=project_id,
source_application="automate_function",
)
)
# self._closure_tree = base["__closure"] # self._closure_tree = base["__closure"]
print( print(
f"It took {self.elapsed():.2f} seconds to receive", f"It took {self.elapsed():.2f} seconds to receive",
@@ -493,29 +480,29 @@ class AutomationContext:
Args: Args:
level: Result level. level: Result level.
category (str): A short tag for the event type. category (str): A short tag for the event type.
affected_objects (Union[Base, List[Base]]): A single object, a list of affected_objects (Union[Base, List[Base]]): A single object or a list of
objects, or an empty list. When empty, a result case is still objects that are causing the info case.
appended with no object IDs (e.g. for skipped rules or version-level
messages).
message (Optional[str]): Optional message. message (Optional[str]): Optional message.
metadata: User provided metadata key value pairs metadata: User provided metadata key value pairs
visual_overrides: Case specific 3D visual overrides. visual_overrides: Case specific 3D visual overrides.
""" """
if isinstance(affected_objects, list): if isinstance(affected_objects, list):
if len(affected_objects) < 1:
raise ValueError(
f"Need atleast one object to report a(n) {level.value.upper()}"
)
object_list = affected_objects object_list = affected_objects
else: else:
object_list = [affected_objects] object_list = [affected_objects]
ids: Dict[str, Optional[str]] = {} ids: Dict[str, Optional[str]] = {}
# When objects are provided, each must have an id (empty list allowed for
# version-level/skipped results).
for o in object_list: for o in object_list:
if not getattr(o, "id", None): # validate that the Base.id is not None. If its a None, throw an Exception
if not o.id:
raise Exception( raise Exception(
f"You can only attach {level} results to objects with an id." f"You can only attach {level} results to objects with an id."
) )
ids[o.id] = getattr(o, "applicationId", None) ids[o.id] = o.applicationId
print( print(
f"Created new {level.value.upper()}" f"Created new {level.value.upper()}"
f" category: {category} caused by: {message}" f" category: {category} caused by: {message}"
+3 -6
View File
@@ -18,7 +18,7 @@ def cmd_line_import() -> None:
parser.add_argument("output_path") parser.add_argument("output_path")
parser.add_argument("project_id") parser.add_argument("project_id")
parser.add_argument("version_message") parser.add_argument("version_message")
parser.add_argument("model_ingestion_id") parser.add_argument("model_id")
# parser.add_argument("model_name") # parser.add_argument("model_name")
# parser.add_argument("region_name") # parser.add_argument("region_name")
@@ -32,8 +32,6 @@ def cmd_line_import() -> None:
"ifc", "ifc",
) )
client: SpeckleClient | None = None
try: try:
client = SpeckleClient(SERVER_URL, use_ssl=not SERVER_URL.startswith("http://")) client = SpeckleClient(SERVER_URL, use_ssl=not SERVER_URL.startswith("http://"))
client.authenticate_with_token(TOKEN) client.authenticate_with_token(TOKEN)
@@ -43,14 +41,13 @@ def cmd_line_import() -> None:
args.file_path, args.file_path,
project, project,
args.version_message, args.version_message,
args.model_ingestion_id, args.model_id,
client, client,
) )
with open(args.output_path, "w") as f: with open(args.output_path, "w") as f:
json.dump({"success": True, "commitId": version.id}, f) json.dump({"success": True, "commitId": version.id}, f)
except Exception as e: except Exception as e:
stack_trace = traceback.format_exc() error_msg = f"IFC Importer failed with exception:\n{traceback.format_exc()}"
error_msg = f"IFC Importer failed with exception:\n{stack_trace}"
print(error_msg) print(error_msg)
# Write error result # Write error result
@@ -12,23 +12,12 @@ def data_object_to_speckle(
step_element: entity_instance, step_element: entity_instance,
children: list[Base], children: list[Base],
current_storey: str | None = None, current_storey: str | None = None,
parent_element: entity_instance | None = None,
) -> DataObject: ) -> DataObject:
guid = cast(str, step_element.GlobalId) guid = cast(str, step_element.GlobalId)
name = cast(str, step_element.Name or guid) name = cast(str, step_element.Name or guid)
properties = extract_properties(step_element) properties = extract_properties(step_element)
# Add parent ID only if element's parent is also a DataObject (not a Collection)
# Collections are: IfcProject and IfcSpatialStructureElement types
if (
parent_element
and hasattr(parent_element, "GlobalId")
and not parent_element.is_a("IfcProject")
and not parent_element.is_a("IfcSpatialStructureElement")
):
properties["parentApplicationId"] = parent_element.GlobalId
# Add building storey information if available and not a building storey itself # Add building storey information if available and not a building storey itself
if current_storey and not step_element.is_a("IfcBuildingStorey"): if current_storey and not step_element.is_a("IfcBuildingStorey"):
properties["Building Storey"] = current_storey properties["Building Storey"] = current_storey
+1 -7
View File
@@ -51,10 +51,4 @@ def open_ifc(file_path: str) -> file:
def create_geometry_iterator(ifc_file: file | sqlite) -> iterator: def create_geometry_iterator(ifc_file: file | sqlite) -> iterator:
GEOMETRY_LIBRARY = "hybrid-opencascade-cgal" # First OCC then fallback to CGAL return iterator(_create_iterator_settings(), ifc_file, multiprocessing.cpu_count())
return iterator(
_create_iterator_settings(),
ifc_file,
multiprocessing.cpu_count(),
geometry_library=GEOMETRY_LIBRARY, # type: ignore
)
+6 -18
View File
@@ -44,13 +44,9 @@ class ImportJob:
_display_value_cache: dict[int, list[Base]] = field(default_factory=dict) _display_value_cache: dict[int, list[Base]] = field(default_factory=dict)
"""Maps an instance step ID to a list of instances""" """Maps an instance step ID to a list of instances"""
def convert_element( def convert_element(self, step_element: entity_instance) -> Base:
self,
step_element: entity_instance,
parent_element: entity_instance | None = None,
) -> Base:
try: try:
return self._convert_element(step_element, parent_element) return self._convert_element(step_element)
except SpeckleException: except SpeckleException:
raise raise
except Exception as ex: except Exception as ex:
@@ -58,18 +54,14 @@ class ImportJob:
f"Failed to convert {step_element.is_a()} #{step_element.id()}" f"Failed to convert {step_element.is_a()} #{step_element.id()}"
) from ex ) from ex
def _convert_element( def _convert_element(self, step_element: entity_instance) -> Base:
self,
step_element: entity_instance,
parent_element: entity_instance | None = None,
) -> Base:
# Track current storey context and store for level proxies # Track current storey context and store for level proxies
previous_storey_data_object = self._current_storey_data_object previous_storey_data_object = self._current_storey_data_object
if step_element.is_a("IfcBuildingStorey"): if step_element.is_a("IfcBuildingStorey"):
# Convert the building storey to a DataObject for the level proxy # Convert the building storey to a DataObject for the level proxy
storey_display_value = self._display_value_cache.get(step_element.id(), []) storey_display_value = self._display_value_cache.get(step_element.id(), [])
self._current_storey_data_object = data_object_to_speckle( self._current_storey_data_object = data_object_to_speckle(
storey_display_value, step_element, [], parent_element=None storey_display_value, step_element, []
) )
children = self._convert_children(step_element) children = self._convert_children(step_element)
@@ -94,11 +86,7 @@ class ImportJob:
) )
else: else:
result = data_object_to_speckle( result = data_object_to_speckle(
display_value, display_value, step_element, children, current_storey_name
step_element,
children,
current_storey_name,
parent_element,
) )
# Associate non-spatial elements with current storey for level proxies # Associate non-spatial elements with current storey for level proxies
if self._current_storey_data_object is not None and result.applicationId: if self._current_storey_data_object is not None and result.applicationId:
@@ -112,7 +100,7 @@ class ImportJob:
def _convert_children(self, step_element: entity_instance) -> list[Base]: def _convert_children(self, step_element: entity_instance) -> list[Base]:
return [ return [
self.convert_element(i, parent_element=step_element) self.convert_element(i)
for i in get_children(step_element) for i in get_children(step_element)
if self._should_convert(i) if self._should_convert(i)
] ]
+34 -101
View File
@@ -1,19 +1,9 @@
import contextlib
import importlib.metadata
import time import time
import traceback
from pathlib import Path
from speckleifc.ifc_geometry_processing import open_ifc from speckleifc.ifc_geometry_processing import open_ifc
from speckleifc.importer import ImportJob from speckleifc.importer import ImportJob
from specklepy.core.api.client import SpeckleClient from specklepy.core.api.client import SpeckleClient
from specklepy.core.api.inputs.model_ingestion_inputs import ( from specklepy.core.api.inputs.version_inputs import CreateVersionInput
ModelIngestionFailedInput,
ModelIngestionStartProcessingInput,
ModelIngestionSuccessInput,
ModelIngestionUpdateInput,
SourceDataInput,
)
from specklepy.core.api.models.current import Project, Version from specklepy.core.api.models.current import Project, Version
from specklepy.core.api.operations import send from specklepy.core.api.operations import send
from specklepy.logging import metrics from specklepy.logging import metrics
@@ -23,106 +13,49 @@ from specklepy.transports.server import ServerTransport
def open_and_convert_file( def open_and_convert_file(
file_path: str, file_path: str,
project: Project, project: Project,
version_message: str, version_message: str | None,
model_ingestion_id: str, model_id: str,
client: SpeckleClient, client: SpeckleClient,
) -> Version: ) -> Version:
try: start = time.time()
start = time.time() very_start = start
very_start = start
path = Path(file_path)
specklepy_version = importlib.metadata.version("specklepy") account = client.account
client.model_ingestion.start_processing( server_url = account.serverInfo.url
ModelIngestionStartProcessingInput( assert server_url
project_id=project.id, remote_transport = ServerTransport(project.id, account=account)
ingestion_id=model_ingestion_id,
progress_message="Importing IFC file",
source_data=SourceDataInput(
file_name=path.name,
file_size_bytes=path.stat().st_size,
source_application_slug=metrics.HOST_APP,
source_application_version=specklepy_version,
),
)
)
account = client.account ifc_file = open_ifc(file_path) # pyright: ignore[reportUnknownVariableType]
server_url = account.serverInfo.url
assert server_url
remote_transport = ServerTransport(project.id, account=account)
ifc_file = open_ifc(file_path) # pyright: ignore[reportUnknownVariableType] import_job = ImportJob(ifc_file) # pyright: ignore[reportUnknownArgumentType]
data = import_job.convert()
client.model_ingestion.update_progress( print(f"File conversion complete after {(time.time() - start) * 1000}ms")
ModelIngestionUpdateInput(
project_id=project.id,
ingestion_id=model_ingestion_id,
progress_message="Converting file",
progress=None,
)
)
import_job = ImportJob(ifc_file) # pyright: ignore[reportUnknownArgumentType]
data = import_job.convert()
print(f"File conversion complete after {(time.time() - start) * 1000}ms") start = time.time()
start = time.time() root_id = send(data, transports=[remote_transport], use_default_cache=False)
print(f"Sending to speckle complete after: {(time.time() - start) * 1000}ms")
client.model_ingestion.update_progress( start = time.time()
ModelIngestionUpdateInput(
project_id=project.id,
ingestion_id=model_ingestion_id,
progress_message="Uploading objects",
progress=None,
)
)
root_id = send(data, transports=[remote_transport], use_default_cache=False)
print(f"Sending to speckle complete after: {(time.time() - start) * 1000}ms")
start = time.time() create_version = CreateVersionInput(
object_id=root_id,
model_id=model_id,
project_id=project.id,
message=version_message,
source_application="ifc",
)
version = client.version.create(create_version)
end = time.time()
print(f"Version committed after: {(end - start) * 1000}ms")
version_id = client.model_ingestion.complete( print(f"Total time (to commit): {(end - very_start) * 1000}ms")
ModelIngestionSuccessInput( del ifc_file
project_id=project.id,
ingestion_id=model_ingestion_id,
root_object_id=root_id,
version_message=version_message,
)
)
# needed to query version until ingestion api expands to serve it custom_properties = {"ui": "dui3", "actionSource": "import"}
version = client.version.get(version_id, project.id) if project.workspace_id:
custom_properties["workspace_id"] = project.workspace_id
metrics.track(metrics.SEND, account, custom_properties, send_sync=True)
end = time.time() return version
print(f"Version committed after: {(end - start) * 1000}ms")
print(f"Total time (to commit): {(end - very_start) * 1000}ms")
del ifc_file
custom_properties = {"ui": "dui3", "actionSource": "import"}
if project.workspace_id:
custom_properties["workspace_id"] = project.workspace_id
metrics.track(
metrics.SEND,
account,
custom_properties,
send_sync=True,
track_email=True,
)
return version
except Exception as e:
stack_trace = traceback.format_exc()
with contextlib.suppress(Exception):
# make sure to not report process kills when we're cancelling
client.model_ingestion.fail_with_error(
ModelIngestionFailedInput(
project_id=project.id,
ingestion_id=model_ingestion_id,
error_reason=str(e),
error_stacktrace=stack_trace,
)
)
raise e
-7
View File
@@ -4,7 +4,6 @@ from specklepy.api.credentials import Account
from specklepy.api.resources import ( from specklepy.api.resources import (
ActiveUserResource, ActiveUserResource,
FileImportResource, FileImportResource,
ModelIngestionResource,
ModelResource, ModelResource,
OtherUserResource, OtherUserResource,
ProjectInviteResource, ProjectInviteResource,
@@ -120,12 +119,6 @@ class SpeckleClient(CoreSpeckleClient):
client=self.httpclient, client=self.httpclient,
server_version=server_version, server_version=server_version,
) )
self.model_ingestion = ModelIngestionResource(
account=self.account,
basepath=self.url,
client=self.httpclient,
server_version=server_version,
)
self.file_import = FileImportResource( self.file_import = FileImportResource(
account=self.account, account=self.account,
basepath=self.url, basepath=self.url,
-5
View File
@@ -1,8 +1,5 @@
from specklepy.api.resources.current.active_user_resource import ActiveUserResource from specklepy.api.resources.current.active_user_resource import ActiveUserResource
from specklepy.api.resources.current.file_import_resource import FileImportResource from specklepy.api.resources.current.file_import_resource import FileImportResource
from specklepy.api.resources.current.model_ingestion_resource import (
ModelIngestionResource,
)
from specklepy.api.resources.current.model_resource import ModelResource from specklepy.api.resources.current.model_resource import ModelResource
from specklepy.api.resources.current.other_user_resource import OtherUserResource from specklepy.api.resources.current.other_user_resource import OtherUserResource
from specklepy.api.resources.current.project_invite_resource import ( from specklepy.api.resources.current.project_invite_resource import (
@@ -25,6 +22,4 @@ __all__ = [
"SubscriptionResource", "SubscriptionResource",
"VersionResource", "VersionResource",
"WorkspaceResource", "WorkspaceResource",
"FileImportResource",
"ModelIngestionResource",
] ]
@@ -15,7 +15,7 @@ from specklepy.logging import metrics
class FileImportResource(CoreResource): class FileImportResource(CoreResource):
"""API Access class for file imports""" """API Access class for projects"""
def __init__(self, account, basepath, client, server_version) -> None: def __init__(self, account, basepath, client, server_version) -> None:
super().__init__( super().__init__(

Some files were not shown because too many files have changed in this diff Show More