pybc365 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pybc365-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Konspec - FPA
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
pybc365-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,197 @@
1
+ Metadata-Version: 2.4
2
+ Name: pybc365
3
+ Version: 0.1.0
4
+ Summary: A modern, fully typed Python client for Microsoft Dynamics 365 Business Central.
5
+ Keywords: business-central,dynamics-365,microsoft-dynamics,erp,api,odata,python
6
+ Author: Akshay Prabhu
7
+ Author-email: Akshay Prabhu <akshay.prabhu@konspec.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 2 - Pre-Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3 :: Only
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Classifier: Topic :: Office/Business :: Financial
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Classifier: Typing :: Typed
21
+ Requires-Dist: httpx>=0.28.1
22
+ Requires-Python: >=3.12
23
+ Project-URL: Homepage, https://github.com/konfpa/pybc365
24
+ Project-URL: Repository, https://github.com/konfpa/pybc365
25
+ Project-URL: Issues, https://github.com/konfpa/pybc365/issues
26
+ Description-Content-Type: text/markdown
27
+
28
+ # pybc365
29
+
30
+ A modern, fully typed Python client for Microsoft Dynamics 365 Business Central.
31
+
32
+ [![CI](https://github.com/konfpa/pybc365/actions/workflows/ci.yml/badge.svg)](https://github.com/konfpa/pybc365/actions/workflows/ci.yml)
33
+ [![PyPI](https://img.shields.io/pypi/v/pybc365.svg)](https://pypi.org/project/pybc365/)
34
+ [![Python versions](https://img.shields.io/pypi/pyversions/pybc365.svg)](https://pypi.org/project/pybc365/)
35
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
36
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
37
+ [![Checked with mypy](https://www.mypy-lang.org/static/mypy_badge.svg)](https://mypy-lang.org/)
38
+
39
+ ## What is pybc365?
40
+
41
+ `pybc365` is a Python library for talking to Microsoft Dynamics 365
42
+ Business Central. It's meant to be a dependable building block for the
43
+ kinds of code that need to move data in and out of BC: applications,
44
+ automation scripts, ETL/data pipelines, and backend services.
45
+
46
+ The library is generic by design: it does not assume anything about a
47
+ particular Business Central tenant, company, or set of custom entities.
48
+
49
+ ## Why pybc365?
50
+
51
+ - **Python-friendly** - designed to feel natural in Python code, not like
52
+ a thin wrapper around BC's OData/REST conventions.
53
+ - **Fully typed** - the whole library ships type annotations and a
54
+ `py.typed` marker, so type checkers understand it out of the box.
55
+ - **Explicit over magical** - no hidden metaclass tricks or implicit
56
+ global state; what the code does is visible in the code.
57
+ - **Minimal assumptions** - no baked-in entities, companies, or
58
+ environment configuration.
59
+ - **Composable and minimal dependencies** - suitable for use as
60
+ infrastructure inside larger systems.
61
+
62
+ `httpx` is the only runtime dependency. See
63
+ [Project status](#project-status) for what v0.1 covers.
64
+
65
+ ## Installation
66
+
67
+ ```bash
68
+ pip install pybc365
69
+ ```
70
+
71
+ or, with uv:
72
+
73
+ ```bash
74
+ uv add pybc365
75
+ ```
76
+
77
+ `pybc365` itself does not require uv; it's a normal PyPI package.
78
+
79
+ ## Quick start
80
+
81
+ ```python
82
+ import httpx
83
+
84
+ from pybc365 import Client, F, desc
85
+
86
+ with Client(
87
+ base_url="https://bc.example.com/BC240",
88
+ company="CRONUS International Ltd.",
89
+ auth=httpx.BasicAuth("webservice", "hunter2"),
90
+ ) as client:
91
+ customers = (
92
+ client.service("Customers")
93
+ .filter(F.Name.startswith("Contoso"), F.Blocked == "")
94
+ .select("No", "Name", "Balance")
95
+ .order_by(desc("Balance"))
96
+ )
97
+
98
+ print(customers.count())
99
+
100
+ for row in customers:
101
+ print(row["No"], row["Name"], row["Balance"])
102
+ ```
103
+
104
+ `base_url` is the server root, stopping **before** the `ODataV4` segment.
105
+ Construction sends no request; the first request goes out when the loop asks
106
+ for its first row.
107
+
108
+ Reading amounts? Pass `parse_float=Decimal`. The default `float` loses the
109
+ digits of an `Edm.Decimal` before your code ever sees them. See
110
+ [Types on the wire](./docs/types-on-the-wire.md).
111
+
112
+ If `Client` collides with `httpx.Client` in your module, import the package
113
+ instead and write `pybc365.Client`.
114
+
115
+ ## Documentation
116
+
117
+ The [documentation](./docs/index.md) ships in this repository.
118
+
119
+ | Page | Answers |
120
+ |---|---|
121
+ | [Getting started](./docs/getting-started.md) | How do I install it and read my first rows? |
122
+ | [Authentication](./docs/authentication.md) | Which auth recipe is tested, and against what? |
123
+ | [Queries](./docs/queries.md) | How do I select, order, page and count? |
124
+ | [Filters](./docs/filters.md) | What literal does my value become, and what does Business Central refuse? |
125
+ | [Types on the wire](./docs/types-on-the-wire.md) | What Python type will this value be? |
126
+ | [Errors](./docs/errors.md) | Which exception is this, and what caused it? |
127
+ | [Not shipped: the REST surface](./docs/rest-surface.md) | Why is there no `/api/v2.0` client? |
128
+ | [API reference](./docs/api.md) | The full signature of every public name. |
129
+
130
+ ## Project status
131
+
132
+ **v0.1: sync, fully typed, read-only.** The library reads published web
133
+ services (OData V4) from one company on one server, with filters, selection,
134
+ ordering, server-driven paging, retry and one exception hierarchy.
135
+
136
+ Not in v0.1: create, update and delete; an async client; the REST API
137
+ (`/api/v2.0`); `$expand`; and any fetch of `$metadata`. `AsyncClient` is the
138
+ reserved name for the async client, and no such class ships today.
139
+
140
+ "Fully typed" means the library checks the **call shape** and nothing about the
141
+ **data**. A misspelled web service, field, projection or ordering key all pass a
142
+ strict `mypy` check with `dict[str, Any]` rows. A `row=` converter is the only
143
+ thing that closes that gap.
144
+
145
+ Every Business Central fact in this documentation is an observation of **BC
146
+ 22.2 on-premises**, never a Business Central guarantee.
147
+
148
+ `pybc365` is not affiliated with or endorsed by Microsoft.
149
+
150
+ ## Design principles
151
+
152
+ - Typed by default, everywhere in the public API.
153
+ - Explicit over magical.
154
+ - Minimal runtime dependencies.
155
+ - Endpoint-agnostic: no assumptions about specific BC entities or setups.
156
+ - Composable pieces over one large client object.
157
+ - Predictable, deliberately designed public APIs.
158
+ - Testable architecture, with behavior-focused tests.
159
+
160
+ ## Development
161
+
162
+ ```bash
163
+ git clone https://github.com/konfpa/pybc365.git
164
+ cd pybc365
165
+ uv sync
166
+ uv run prek install
167
+ ```
168
+
169
+ Common commands:
170
+
171
+ ```bash
172
+ uv run ruff format . # format
173
+ uv run ruff check . # lint
174
+ uv run mypy src scripts tests # type check
175
+ uv run pytest # test (with coverage)
176
+ uv run prek run --all-files # all pre-commit hooks
177
+ ```
178
+
179
+ See [`AGENTS.md`](./AGENTS.md) for the fuller set of engineering
180
+ principles and quality expectations this project follows.
181
+
182
+ ## Contributing
183
+
184
+ Contributions are welcome. Please read [`AGENTS.md`](./AGENTS.md) for the
185
+ project's engineering principles before opening a pull request, and see
186
+ [`CONTRIBUTING.md`](./CONTRIBUTING.md) for the contribution workflow.
187
+
188
+ ## Security
189
+
190
+ See [`SECURITY.md`](./SECURITY.md) for how to report a vulnerability.
191
+
192
+ ## License
193
+
194
+ Licensed under the [MIT License](./LICENSE).
195
+
196
+ `pybc365` is an independent open-source project and is not affiliated
197
+ with or endorsed by Microsoft.
@@ -0,0 +1,170 @@
1
+ # pybc365
2
+
3
+ A modern, fully typed Python client for Microsoft Dynamics 365 Business Central.
4
+
5
+ [![CI](https://github.com/konfpa/pybc365/actions/workflows/ci.yml/badge.svg)](https://github.com/konfpa/pybc365/actions/workflows/ci.yml)
6
+ [![PyPI](https://img.shields.io/pypi/v/pybc365.svg)](https://pypi.org/project/pybc365/)
7
+ [![Python versions](https://img.shields.io/pypi/pyversions/pybc365.svg)](https://pypi.org/project/pybc365/)
8
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
9
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
10
+ [![Checked with mypy](https://www.mypy-lang.org/static/mypy_badge.svg)](https://mypy-lang.org/)
11
+
12
+ ## What is pybc365?
13
+
14
+ `pybc365` is a Python library for talking to Microsoft Dynamics 365
15
+ Business Central. It's meant to be a dependable building block for the
16
+ kinds of code that need to move data in and out of BC: applications,
17
+ automation scripts, ETL/data pipelines, and backend services.
18
+
19
+ The library is generic by design: it does not assume anything about a
20
+ particular Business Central tenant, company, or set of custom entities.
21
+
22
+ ## Why pybc365?
23
+
24
+ - **Python-friendly** - designed to feel natural in Python code, not like
25
+ a thin wrapper around BC's OData/REST conventions.
26
+ - **Fully typed** - the whole library ships type annotations and a
27
+ `py.typed` marker, so type checkers understand it out of the box.
28
+ - **Explicit over magical** - no hidden metaclass tricks or implicit
29
+ global state; what the code does is visible in the code.
30
+ - **Minimal assumptions** - no baked-in entities, companies, or
31
+ environment configuration.
32
+ - **Composable and minimal dependencies** - suitable for use as
33
+ infrastructure inside larger systems.
34
+
35
+ `httpx` is the only runtime dependency. See
36
+ [Project status](#project-status) for what v0.1 covers.
37
+
38
+ ## Installation
39
+
40
+ ```bash
41
+ pip install pybc365
42
+ ```
43
+
44
+ or, with uv:
45
+
46
+ ```bash
47
+ uv add pybc365
48
+ ```
49
+
50
+ `pybc365` itself does not require uv; it's a normal PyPI package.
51
+
52
+ ## Quick start
53
+
54
+ ```python
55
+ import httpx
56
+
57
+ from pybc365 import Client, F, desc
58
+
59
+ with Client(
60
+ base_url="https://bc.example.com/BC240",
61
+ company="CRONUS International Ltd.",
62
+ auth=httpx.BasicAuth("webservice", "hunter2"),
63
+ ) as client:
64
+ customers = (
65
+ client.service("Customers")
66
+ .filter(F.Name.startswith("Contoso"), F.Blocked == "")
67
+ .select("No", "Name", "Balance")
68
+ .order_by(desc("Balance"))
69
+ )
70
+
71
+ print(customers.count())
72
+
73
+ for row in customers:
74
+ print(row["No"], row["Name"], row["Balance"])
75
+ ```
76
+
77
+ `base_url` is the server root, stopping **before** the `ODataV4` segment.
78
+ Construction sends no request; the first request goes out when the loop asks
79
+ for its first row.
80
+
81
+ Reading amounts? Pass `parse_float=Decimal`. The default `float` loses the
82
+ digits of an `Edm.Decimal` before your code ever sees them. See
83
+ [Types on the wire](./docs/types-on-the-wire.md).
84
+
85
+ If `Client` collides with `httpx.Client` in your module, import the package
86
+ instead and write `pybc365.Client`.
87
+
88
+ ## Documentation
89
+
90
+ The [documentation](./docs/index.md) ships in this repository.
91
+
92
+ | Page | Answers |
93
+ |---|---|
94
+ | [Getting started](./docs/getting-started.md) | How do I install it and read my first rows? |
95
+ | [Authentication](./docs/authentication.md) | Which auth recipe is tested, and against what? |
96
+ | [Queries](./docs/queries.md) | How do I select, order, page and count? |
97
+ | [Filters](./docs/filters.md) | What literal does my value become, and what does Business Central refuse? |
98
+ | [Types on the wire](./docs/types-on-the-wire.md) | What Python type will this value be? |
99
+ | [Errors](./docs/errors.md) | Which exception is this, and what caused it? |
100
+ | [Not shipped: the REST surface](./docs/rest-surface.md) | Why is there no `/api/v2.0` client? |
101
+ | [API reference](./docs/api.md) | The full signature of every public name. |
102
+
103
+ ## Project status
104
+
105
+ **v0.1: sync, fully typed, read-only.** The library reads published web
106
+ services (OData V4) from one company on one server, with filters, selection,
107
+ ordering, server-driven paging, retry and one exception hierarchy.
108
+
109
+ Not in v0.1: create, update and delete; an async client; the REST API
110
+ (`/api/v2.0`); `$expand`; and any fetch of `$metadata`. `AsyncClient` is the
111
+ reserved name for the async client, and no such class ships today.
112
+
113
+ "Fully typed" means the library checks the **call shape** and nothing about the
114
+ **data**. A misspelled web service, field, projection or ordering key all pass a
115
+ strict `mypy` check with `dict[str, Any]` rows. A `row=` converter is the only
116
+ thing that closes that gap.
117
+
118
+ Every Business Central fact in this documentation is an observation of **BC
119
+ 22.2 on-premises**, never a Business Central guarantee.
120
+
121
+ `pybc365` is not affiliated with or endorsed by Microsoft.
122
+
123
+ ## Design principles
124
+
125
+ - Typed by default, everywhere in the public API.
126
+ - Explicit over magical.
127
+ - Minimal runtime dependencies.
128
+ - Endpoint-agnostic: no assumptions about specific BC entities or setups.
129
+ - Composable pieces over one large client object.
130
+ - Predictable, deliberately designed public APIs.
131
+ - Testable architecture, with behavior-focused tests.
132
+
133
+ ## Development
134
+
135
+ ```bash
136
+ git clone https://github.com/konfpa/pybc365.git
137
+ cd pybc365
138
+ uv sync
139
+ uv run prek install
140
+ ```
141
+
142
+ Common commands:
143
+
144
+ ```bash
145
+ uv run ruff format . # format
146
+ uv run ruff check . # lint
147
+ uv run mypy src scripts tests # type check
148
+ uv run pytest # test (with coverage)
149
+ uv run prek run --all-files # all pre-commit hooks
150
+ ```
151
+
152
+ See [`AGENTS.md`](./AGENTS.md) for the fuller set of engineering
153
+ principles and quality expectations this project follows.
154
+
155
+ ## Contributing
156
+
157
+ Contributions are welcome. Please read [`AGENTS.md`](./AGENTS.md) for the
158
+ project's engineering principles before opening a pull request, and see
159
+ [`CONTRIBUTING.md`](./CONTRIBUTING.md) for the contribution workflow.
160
+
161
+ ## Security
162
+
163
+ See [`SECURITY.md`](./SECURITY.md) for how to report a vulnerability.
164
+
165
+ ## License
166
+
167
+ Licensed under the [MIT License](./LICENSE).
168
+
169
+ `pybc365` is an independent open-source project and is not affiliated
170
+ with or endorsed by Microsoft.
@@ -0,0 +1,184 @@
1
+ [project]
2
+ name = "pybc365"
3
+ version = "0.1.0"
4
+ description = "A modern, fully typed Python client for Microsoft Dynamics 365 Business Central."
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ license-files = ["LICENSE"]
8
+ requires-python = ">=3.12"
9
+ keywords = [
10
+ "business-central",
11
+ "dynamics-365",
12
+ "microsoft-dynamics",
13
+ "erp",
14
+ "api",
15
+ "odata",
16
+ "python",
17
+ ]
18
+ classifiers = [
19
+ "Development Status :: 2 - Pre-Alpha",
20
+ "Intended Audience :: Developers",
21
+ "Operating System :: OS Independent",
22
+ "Programming Language :: Python :: 3",
23
+ "Programming Language :: Python :: 3 :: Only",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Programming Language :: Python :: 3.13",
26
+ "Programming Language :: Python :: 3.14",
27
+ "Topic :: Office/Business :: Financial",
28
+ "Topic :: Software Development :: Libraries :: Python Modules",
29
+ "Typing :: Typed",
30
+ ]
31
+ dependencies = ["httpx>=0.28.1"]
32
+
33
+ [[project.authors]]
34
+ name = "Akshay Prabhu"
35
+ email = "akshay.prabhu@konspec.com"
36
+
37
+ [project.urls]
38
+ Homepage = "https://github.com/konfpa/pybc365"
39
+ Repository = "https://github.com/konfpa/pybc365"
40
+ Issues = "https://github.com/konfpa/pybc365/issues"
41
+
42
+ [build-system]
43
+ requires = ["uv_build>=0.12.6,<0.13.0"]
44
+ build-backend = "uv_build"
45
+
46
+ [dependency-groups]
47
+ dev = [
48
+ "httpx-ntlm>=1.4.0",
49
+ "mypy>=2.3.1",
50
+ "prek>=0.5.2",
51
+ "pytest>=9.1.1",
52
+ "pytest-cov>=7.1.0",
53
+ "ruff>=0.16.6",
54
+ ]
55
+
56
+ [tool.ruff]
57
+ target-version = "py312"
58
+ src = ["src"]
59
+ preview = true
60
+
61
+ [tool.ruff.lint]
62
+ select = [
63
+ "E",
64
+ "W",
65
+ "F",
66
+ "I",
67
+ "N",
68
+ "UP",
69
+ "B",
70
+ "C4",
71
+ "SIM",
72
+ "RET",
73
+ "PTH",
74
+ "ANN",
75
+ "BLE",
76
+ "EM",
77
+ "TRY",
78
+ "RSE",
79
+ "T10",
80
+ "PT",
81
+ "ARG",
82
+ "TID",
83
+ "ISC",
84
+ "PIE",
85
+ "TC",
86
+ "ERA",
87
+ "C90",
88
+ "D",
89
+ "DOC",
90
+ "A",
91
+ "S",
92
+ "FBT",
93
+ "DTZ",
94
+ "LOG",
95
+ "G",
96
+ "INP",
97
+ "SLF",
98
+ "SLOT",
99
+ "PL",
100
+ "PERF",
101
+ "FURB",
102
+ "PGH",
103
+ "EXE",
104
+ "ICN",
105
+ "PYI",
106
+ "ASYNC",
107
+ "T20",
108
+ "YTT",
109
+ "INT",
110
+ "FLY",
111
+ "RUF",
112
+ "multi-line-summary-second-line",
113
+ ]
114
+ ignore = [
115
+ "single-line-implicit-string-concatenation",
116
+ "raise-vanilla-args",
117
+ "docstring-extraneous-exception",
118
+ "multi-line-summary-first-line",
119
+ ]
120
+
121
+ [tool.ruff.lint.per-file-ignores]
122
+ "scripts/*.py" = ["implicit-namespace-package"]
123
+ "tests/**/*.py" = [
124
+ "ARG",
125
+ "assert",
126
+ "implicit-namespace-package",
127
+ "private-member-access",
128
+ "magic-value-comparison",
129
+ "import-private-name",
130
+ "import-outside-top-level",
131
+ "compare-to-empty-string",
132
+ "float-equality-comparison",
133
+ ]
134
+
135
+ [tool.ruff.lint.isort]
136
+ known-first-party = ["pybc365"]
137
+
138
+ [tool.ruff.lint.mccabe]
139
+ max-complexity = 10
140
+
141
+ [tool.ruff.lint.pydocstyle]
142
+ convention = "google"
143
+
144
+ [tool.ruff.lint.pylint]
145
+ max-args = 8
146
+
147
+ [tool.ruff.format]
148
+ docstring-code-format = true
149
+ exclude = ["*.md"]
150
+
151
+ [tool.mypy]
152
+ python_version = "3.12"
153
+ strict = true
154
+ warn_unreachable = true
155
+ enable_error_code = [
156
+ "ignore-without-code",
157
+ "redundant-expr",
158
+ "possibly-undefined",
159
+ ]
160
+
161
+ [[tool.mypy.overrides]]
162
+ module = ["httpx_ntlm.*"]
163
+ ignore_missing_imports = true
164
+
165
+ [tool.pytest.ini_options]
166
+ addopts = [
167
+ "--cov=pybc365",
168
+ "--cov-report=term-missing",
169
+ "--cov-fail-under=95",
170
+ "-m",
171
+ "not live",
172
+ ]
173
+ testpaths = ["tests"]
174
+ markers = ["live: reads a real Business Central instance. Run with -m live."]
175
+
176
+ [tool.coverage.run]
177
+ source = ["pybc365"]
178
+ branch = true
179
+
180
+ [tool.coverage.report]
181
+ exclude_lines = [
182
+ "pragma: no cover",
183
+ "if TYPE_CHECKING:",
184
+ ]