Skip to content

Commit b30b4fb

Browse files
committed
Add Microsoft Python standards compliance
- Add py.typed markers for both packages (enables type checking) - Enhance pyproject.toml with complete metadata and dev dependencies - Add missing docstrings for HttpClient, error_codes functions - Add module docstrings for better discoverability - Configure linting tools (black, isort, mypy, ruff) per Microsoft standards
1 parent 41f8842 commit b30b4fb

5 files changed

Lines changed: 119 additions & 1 deletion

File tree

pyproject.toml

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,22 @@ version = "0.1.0b1"
88
description = "Python SDK for Microsoft Dataverse"
99
readme = {file = "README.md", content-type = "text/markdown"}
1010
authors = [{name = "Microsoft Corporation"}]
11-
license = "MIT"
11+
license = {text = "MIT"}
1212
license-files = ["LICENSE"]
1313
requires-python = ">=3.10"
14+
keywords = ["dataverse", "powerapps", "powerplatform", "crm", "dynamics", "odata"]
1415
classifiers = [
16+
"Development Status :: 4 - Beta",
17+
"Intended Audience :: Developers",
18+
"License :: OSI Approved :: MIT License",
19+
"Programming Language :: Python :: 3",
1520
"Programming Language :: Python :: 3.10",
1621
"Programming Language :: Python :: 3.11",
1722
"Programming Language :: Python :: 3.12",
1823
"Programming Language :: Python :: 3.13",
1924
"Operating System :: OS Independent",
25+
"Topic :: Software Development :: Libraries :: Python Modules",
26+
"Typing :: Typed",
2027
]
2128
dependencies = [
2229
"azure-identity>=1.17.0",
@@ -27,6 +34,23 @@ dependencies = [
2734

2835
[project.urls]
2936
"Homepage" = "https://github.com/microsoft/PowerPlatform-DataverseClient-Python"
37+
"Repository" = "https://github.com/microsoft/PowerPlatform-DataverseClient-Python.git"
38+
"Issues" = "https://github.com/microsoft/PowerPlatform-DataverseClient-Python/issues"
39+
"Documentation" = "https://github.com/microsoft/PowerPlatform-DataverseClient-Python#readme"
40+
41+
[tool.setuptools]
42+
package-dir = {"" = "src"}
43+
zip-safe = false
44+
45+
[project.optional-dependencies]
46+
dev = [
47+
"pytest>=7.0.0",
48+
"pytest-cov>=4.0.0",
49+
"black>=23.0.0",
50+
"isort>=5.12.0",
51+
"mypy>=1.0.0",
52+
"ruff>=0.1.0",
53+
]
3054

3155
[tool.setuptools]
3256
package-dir = {"" = "src"}
@@ -36,3 +60,33 @@ zip-safe = false
3660
where = ["src"]
3761
include = ["PowerPlatform*"]
3862
namespaces = false
63+
64+
[tool.setuptools.package-data]
65+
"*" = ["py.typed"]
66+
67+
# Microsoft Python Standards - Linting & Formatting
68+
[tool.black]
69+
line-length = 120
70+
target-version = ['py310']
71+
72+
[tool.isort]
73+
profile = "black"
74+
line_length = 120
75+
76+
[tool.mypy]
77+
python_version = "3.10"
78+
strict = true
79+
warn_return_any = true
80+
warn_unused_configs = true
81+
82+
[tool.ruff]
83+
line-length = 120
84+
target-version = "py310"
85+
select = [
86+
"E", "W", # pycodestyle
87+
"F", # pyflakes
88+
"I", # isort
89+
"N", # pep8-naming
90+
"UP", # pyupgrade
91+
"B", # flake8-bugbear
92+
]

src/PowerPlatform/Dataverse/core/error_codes.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
# Copyright (c) Microsoft Corporation.
22
# Licensed under the MIT license.
33

4+
"""
5+
Error code constants and utilities for Dataverse SDK exceptions.
6+
7+
This module defines error subcodes used throughout the SDK for categorizing
8+
different types of failures, including HTTP errors, validation errors,
9+
SQL parsing errors, and metadata operation errors.
10+
"""
11+
412
# HTTP subcode constants
513
HTTP_400 = "http_400"
614
HTTP_401 = "http_401"
@@ -69,7 +77,26 @@
6977
TRANSIENT_STATUS = {429, 502, 503, 504}
7078

7179
def http_subcode(status: int) -> str:
80+
"""
81+
Convert HTTP status code to error subcode string.
82+
83+
:param status: HTTP status code (e.g., 400, 404, 500).
84+
:type status: int
85+
:return: Error subcode string (e.g., "http_400", "http_404").
86+
:rtype: str
87+
"""
7288
return HTTP_STATUS_TO_SUBCODE.get(status, f"http_{status}")
7389

7490
def is_transient_status(status: int) -> bool:
91+
"""
92+
Check if an HTTP status code indicates a transient error that may succeed on retry.
93+
94+
Transient status codes include: 429 (Too Many Requests), 502 (Bad Gateway),
95+
503 (Service Unavailable), and 504 (Gateway Timeout).
96+
97+
:param status: HTTP status code to check.
98+
:type status: int
99+
:return: True if the status code is considered transient.
100+
:rtype: bool
101+
"""
75102
return status in TRANSIENT_STATUS

src/PowerPlatform/Dataverse/core/http.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
# Copyright (c) Microsoft Corporation.
22
# Licensed under the MIT license.
33

4+
"""
5+
HTTP client with automatic retry logic and timeout handling.
6+
7+
This module provides :class:`HttpClient`, a wrapper around the requests library
8+
that adds configurable retry behavior for transient network errors and
9+
intelligent timeout management based on HTTP method types.
10+
"""
11+
412
from __future__ import annotations
513

614
import time
@@ -10,6 +18,20 @@
1018

1119

1220
class HttpClient:
21+
"""
22+
HTTP client with configurable retry logic and timeout handling.
23+
24+
Provides automatic retry behavior for transient failures and default timeout
25+
management for different HTTP methods.
26+
27+
:param retries: Maximum number of retry attempts for transient errors. Default is 5.
28+
:type retries: int or None
29+
:param backoff: Base delay in seconds between retry attempts. Default is 0.5.
30+
:type backoff: float or None
31+
:param timeout: Default request timeout in seconds. If None, uses per-method defaults.
32+
:type timeout: float or None
33+
"""
34+
1335
def __init__(
1436
self,
1537
*,
@@ -22,6 +44,21 @@ def __init__(
2244
self.default_timeout: Optional[float] = timeout
2345

2446
def request(self, method: str, url: str, **kwargs: Any) -> requests.Response:
47+
"""
48+
Execute an HTTP request with automatic retry logic and timeout management.
49+
50+
Applies default timeouts based on HTTP method (120s for POST/DELETE, 10s for others)
51+
and retries on network errors with exponential backoff.
52+
53+
:param method: HTTP method (GET, POST, PUT, DELETE, etc.).
54+
:type method: str
55+
:param url: Target URL for the request.
56+
:type url: str
57+
:param kwargs: Additional arguments passed to ``requests.request()``, including headers, data, etc.
58+
:return: HTTP response object.
59+
:rtype: requests.Response
60+
:raises requests.exceptions.RequestException: If all retry attempts fail.
61+
"""
2562
# Apply per-method default timeouts if not provided
2663
# Apply default timeout if not provided; fall back to per-method defaults
2764
if "timeout" not in kwargs:

src/PowerPlatform/Dataverse/py.typed

Whitespace-only changes.

src/dataverse_sdk/py.typed

Whitespace-only changes.

0 commit comments

Comments
 (0)