cdata-connect 0.0.1__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.
Files changed (62) hide show
  1. cdata_connect-0.0.1/.github/workflows/release.yml +146 -0
  2. cdata_connect-0.0.1/.github/workflows/test.yml +57 -0
  3. cdata_connect-0.0.1/.gitignore +51 -0
  4. cdata_connect-0.0.1/CLAUDE.md +51 -0
  5. cdata_connect-0.0.1/EXAMPLES.md +315 -0
  6. cdata_connect-0.0.1/LICENSE +21 -0
  7. cdata_connect-0.0.1/MANIFEST.in +8 -0
  8. cdata_connect-0.0.1/PKG-INFO +287 -0
  9. cdata_connect-0.0.1/README.md +253 -0
  10. cdata_connect-0.0.1/cdata_connect/LICENSE +21 -0
  11. cdata_connect-0.0.1/cdata_connect/README.md +132 -0
  12. cdata_connect-0.0.1/cdata_connect/__init__.py +82 -0
  13. cdata_connect-0.0.1/cdata_connect/_version.py +34 -0
  14. cdata_connect-0.0.1/cdata_connect/connection.py +122 -0
  15. cdata_connect-0.0.1/cdata_connect/cursor.py +368 -0
  16. cdata_connect-0.0.1/cdata_connect/exceptions.py +42 -0
  17. cdata_connect-0.0.1/cdata_connect/log.py +7 -0
  18. cdata_connect-0.0.1/cdata_connect/util/__init__.py +0 -0
  19. cdata_connect-0.0.1/cdata_connect/util/types.py +214 -0
  20. cdata_connect-0.0.1/cdata_connect/version.py +13 -0
  21. cdata_connect-0.0.1/cdata_connect.egg-info/PKG-INFO +287 -0
  22. cdata_connect-0.0.1/cdata_connect.egg-info/SOURCES.txt +61 -0
  23. cdata_connect-0.0.1/cdata_connect.egg-info/dependency_links.txt +1 -0
  24. cdata_connect-0.0.1/cdata_connect.egg-info/requires.txt +11 -0
  25. cdata_connect-0.0.1/cdata_connect.egg-info/top_level.txt +2 -0
  26. cdata_connect-0.0.1/pyproject.toml +85 -0
  27. cdata_connect-0.0.1/requirements.txt +3 -0
  28. cdata_connect-0.0.1/setup.cfg +4 -0
  29. cdata_connect-0.0.1/tests/README.md +112 -0
  30. cdata_connect-0.0.1/tests/__init__.py +0 -0
  31. cdata_connect-0.0.1/tests/conftest.py +148 -0
  32. cdata_connect-0.0.1/tests/integration/__init__.py +0 -0
  33. cdata_connect-0.0.1/tests/integration/conftest.py +201 -0
  34. cdata_connect-0.0.1/tests/integration/test_dbapi20_compliance.py +691 -0
  35. cdata_connect-0.0.1/tests/integration/test_error_scenarios.py +67 -0
  36. cdata_connect-0.0.1/tests/integration/test_query_operations.py +73 -0
  37. cdata_connect-0.0.1/tests/integration/test_stored_procedures.py +27 -0
  38. cdata_connect-0.0.1/tests/integration/test_streaming.py +72 -0
  39. cdata_connect-0.0.1/tests/integration/test_timeout_and_delays.py +251 -0
  40. cdata_connect-0.0.1/tests/integration_live/__init__.py +0 -0
  41. cdata_connect-0.0.1/tests/integration_live/conftest.py +88 -0
  42. cdata_connect-0.0.1/tests/integration_live/helpers.py +82 -0
  43. cdata_connect-0.0.1/tests/integration_live/test_delete.py +49 -0
  44. cdata_connect-0.0.1/tests/integration_live/test_insert.py +67 -0
  45. cdata_connect-0.0.1/tests/integration_live/test_select.py +63 -0
  46. cdata_connect-0.0.1/tests/integration_live/test_stored_procedures.py +199 -0
  47. cdata_connect-0.0.1/tests/integration_live/test_update.py +62 -0
  48. cdata_connect-0.0.1/tests/performance/README.md +206 -0
  49. cdata_connect-0.0.1/tests/performance/__init__.py +0 -0
  50. cdata_connect-0.0.1/tests/performance/conftest.py +134 -0
  51. cdata_connect-0.0.1/tests/performance/test_concurrency.py +194 -0
  52. cdata_connect-0.0.1/tests/performance/test_soak.py +505 -0
  53. cdata_connect-0.0.1/tests/performance/test_streaming.py +273 -0
  54. cdata_connect-0.0.1/tests/performance/test_throughput.py +202 -0
  55. cdata_connect-0.0.1/tests/performance/test_timeout_behavior.py +192 -0
  56. cdata_connect-0.0.1/tests/unit/__init__.py +0 -0
  57. cdata_connect-0.0.1/tests/unit/conftest.py +33 -0
  58. cdata_connect-0.0.1/tests/unit/test_connection.py +259 -0
  59. cdata_connect-0.0.1/tests/unit/test_cursor.py +118 -0
  60. cdata_connect-0.0.1/tests/unit/test_exceptions.py +68 -0
  61. cdata_connect-0.0.1/tests/unit/test_module.py +66 -0
  62. cdata_connect-0.0.1/tests/unit/test_types.py +342 -0
@@ -0,0 +1,146 @@
1
+ name: Release to PyPI
2
+
3
+ # Triggered when a GitHub Release is published (which also creates the git tag).
4
+ # Flow: test → build → publish PyPI → verify
5
+ on:
6
+ release:
7
+ types: [published]
8
+
9
+ jobs:
10
+ # ──────────────────────────────────────────────────────────────────
11
+ # 1. Run unit tests before building
12
+ # ──────────────────────────────────────────────────────────────────
13
+ test:
14
+ name: Test (Python 3.11)
15
+ runs-on: ubuntu-latest
16
+ defaults:
17
+ run:
18
+ working-directory: connector
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+ with:
22
+ fetch-depth: 0 # setuptools-scm needs full history
23
+
24
+ - uses: actions/setup-python@v5
25
+ with:
26
+ python-version: "3.11"
27
+
28
+ - name: Install connector + test deps
29
+ run: |
30
+ pip install --upgrade pip
31
+ pip install -e ".[dev]"
32
+
33
+ - name: Run non-integration tests
34
+ run: |
35
+ pytest tests/ \
36
+ -k "test_apilevel or test_threadsafety or test_paramstyle or test_Exceptions or test_Date or test_Time or test_Timestamp or test_Binary or test_STRING or test_BINARY or test_NUMBER or test_DATETIME or test_ROWID" \
37
+ -v --tb=short
38
+ env:
39
+ CDATA_BASE_URL: http://localhost:8080/api
40
+ SKIP_LIVE_TESTS: "1"
41
+
42
+ # ──────────────────────────────────────────────────────────────────
43
+ # 2. Build wheel + sdist
44
+ # ──────────────────────────────────────────────────────────────────
45
+ build:
46
+ name: Build Distribution
47
+ needs: test
48
+ runs-on: ubuntu-latest
49
+ defaults:
50
+ run:
51
+ working-directory: connector
52
+ steps:
53
+ - uses: actions/checkout@v4
54
+ with:
55
+ fetch-depth: 0 # setuptools-scm needs full history to derive version
56
+
57
+ - uses: actions/setup-python@v5
58
+ with:
59
+ python-version: "3.11"
60
+
61
+ - name: Install build tools
62
+ run: pip install build twine
63
+
64
+ - name: Build wheel and sdist
65
+ run: python -m build
66
+
67
+ - name: Validate distribution
68
+ run: twine check dist/*
69
+
70
+ - name: Smoke-test the wheel in a clean venv
71
+ run: |
72
+ python -m venv /tmp/wheel_test_env
73
+ /tmp/wheel_test_env/bin/pip install dist/*.whl
74
+ /tmp/wheel_test_env/bin/python -c "
75
+ import cdata_connect
76
+ assert cdata_connect.apilevel == '2.0'
77
+ assert cdata_connect.threadsafety == 1
78
+ assert cdata_connect.paramstyle == 'pyformat'
79
+ from cdata_connect import Connection, Error, STRING, BINARY, NUMBER, DATETIME, ROWID
80
+ from cdata_connect.util.types import FIELD_TYPES
81
+ print('Wheel smoke test passed. Version:', cdata_connect.__version__)
82
+ "
83
+
84
+ - name: Upload distribution artifacts
85
+ uses: actions/upload-artifact@v4
86
+ with:
87
+ name: dist-packages
88
+ path: connector/dist/
89
+ retention-days: 7
90
+
91
+ # ──────────────────────────────────────────────────────────────────
92
+ # 3. Publish to production PyPI
93
+ # Gated by the 'pypi' GitHub Environment — add required reviewers in
94
+ # Settings > Environments > pypi > Required reviewers.
95
+ # Uses OIDC Trusted Publishing — no API token required.
96
+ # ──────────────────────────────────────────────────────────────────
97
+ publish:
98
+ name: Publish to PyPI
99
+ needs: build
100
+ runs-on: ubuntu-latest
101
+ environment:
102
+ name: pypi
103
+ url: https://pypi.org/project/cdata-connect/
104
+ permissions:
105
+ id-token: write # Required for Trusted Publishing
106
+
107
+ steps:
108
+ - name: Download distribution artifacts
109
+ uses: actions/download-artifact@v4
110
+ with:
111
+ name: dist-packages
112
+ path: dist/
113
+
114
+ - name: Publish to PyPI
115
+ uses: pypa/gh-action-pypi-publish@release/v1
116
+ with:
117
+ print-hash: true
118
+
119
+ # ──────────────────────────────────────────────────────────────────
120
+ # 4. Post-release verification
121
+ # ──────────────────────────────────────────────────────────────────
122
+ verify:
123
+ name: Verify PyPI Publication
124
+ needs: publish
125
+ runs-on: ubuntu-latest
126
+ steps:
127
+ - name: Determine release version from tag
128
+ id: version
129
+ run: |
130
+ VERSION="${GITHUB_REF_NAME#v}"
131
+ echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
132
+
133
+ - name: Wait for PyPI propagation
134
+ run: sleep 60
135
+
136
+ - name: Install from PyPI and verify
137
+ run: |
138
+ pip install "cdata-connect==${{ steps.version.outputs.version }}"
139
+ python -c "
140
+ import cdata_connect
141
+ print('Installed version:', cdata_connect.__version__)
142
+ assert cdata_connect.apilevel == '2.0'
143
+ assert cdata_connect.threadsafety == 1
144
+ assert cdata_connect.paramstyle == 'pyformat'
145
+ print('Post-release verification passed.')
146
+ "
@@ -0,0 +1,57 @@
1
+ name: Tests
2
+
3
+ on:
4
+ push:
5
+ branches: ["main", "develop"]
6
+ pull_request:
7
+ branches: ["main"]
8
+
9
+ jobs:
10
+ test:
11
+ name: Python ${{ matrix.python-version }}
12
+ runs-on: ubuntu-latest
13
+
14
+ strategy:
15
+ fail-fast: false
16
+ matrix:
17
+ python-version: ["3.10", "3.11", "3.12"]
18
+
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+ with:
22
+ fetch-depth: 0
23
+
24
+ - name: Set up Python ${{ matrix.python-version }}
25
+ uses: actions/setup-python@v5
26
+ with:
27
+ python-version: ${{ matrix.python-version }}
28
+
29
+ - name: Install connector
30
+ working-directory: connector
31
+ run: |
32
+ pip install --upgrade pip
33
+ pip install -e .
34
+ pip install pytest pytest-cov requests
35
+
36
+ - name: Install mock server dependencies
37
+ run: |
38
+ pip install -r ../connect-ai-mock/requirements.txt
39
+ working-directory: connector
40
+ # conftest.py auto-discovers the mock server at ../connect-ai-mock relative to connector/
41
+
42
+ - name: Run tests against mock API
43
+ run: pytest tests/ -v --tb=short
44
+ env:
45
+ CDATA_BASE_URL: http://localhost:8080/api
46
+ CDATA_USERNAME: test@example.com
47
+ CDATA_PASSWORD: any_token
48
+ SKIP_LIVE_TESTS: "1"
49
+ # conftest.py auto-starts the mock server on localhost:8080
50
+
51
+ - name: Upload coverage
52
+ if: matrix.python-version == '3.11'
53
+ uses: actions/upload-artifact@v4
54
+ with:
55
+ name: coverage-report
56
+ path: build/coverage/
57
+ retention-days: 7
@@ -0,0 +1,51 @@
1
+ # setuptools-scm generated version file (written at build time)
2
+ cdata_connect/_version.py
3
+
4
+ # Python bytecode
5
+ __pycache__/
6
+ *.py[cod]
7
+ *.pyo
8
+
9
+ # Distribution / packaging (produced by `python -m build`)
10
+ dist/
11
+ build/
12
+ *.egg-info/
13
+ *.egg
14
+ MANIFEST
15
+
16
+ # Wheel / sdist archives
17
+ *.whl
18
+ *.tar.gz
19
+
20
+ # Virtual environments
21
+ .venv/
22
+ venv/
23
+ env/
24
+ ENV/
25
+
26
+ # Pytest / coverage
27
+ .pytest_cache/
28
+ .coverage
29
+ .coverage.*
30
+ htmlcov/
31
+ coverage.xml
32
+
33
+ # Type checkers
34
+ .mypy_cache/
35
+ .pytype/
36
+ .pyre/
37
+
38
+ # Local pyhocon config (users may create this for local testing)
39
+ config.conf
40
+ *.conf
41
+
42
+
43
+ # Editor / IDE
44
+ .vscode/
45
+ .idea/
46
+ *.swp
47
+ *.swo
48
+
49
+ # OS noise
50
+ .DS_Store
51
+ Thumbs.db
@@ -0,0 +1,51 @@
1
+ # Connector — AI Context
2
+
3
+ This is the installable `cdata-connect` Python package — a DB-API 2.0 (PEP 249) connector for CData Connect AI.
4
+
5
+ ## Source Layout
6
+
7
+ ```
8
+ cdata_connect/
9
+ ├── __init__.py # Module exports, connect() factory, apilevel/threadsafety/paramstyle
10
+ ├── connection.py # Connection class — auth, config, lifecycle, cursor creation
11
+ ├── cursor.py # Cursor class — execute, fetch, streaming (ijson), retry logic
12
+ ├── exceptions.py # PEP 249 exception hierarchy + ConfigurationError
13
+ ├── util/types.py # Type mapping (Connect API ↔ Python), Date/Time/Timestamp/Binary
14
+ ├── log.py # Logger (NullHandler)
15
+ └── version.py # __version__
16
+ ```
17
+
18
+ ## Architecture
19
+
20
+ 1. `connect()` → creates a `Connection` (validates params or reads PyHOCON config)
21
+ 2. `Connection.cursor()` → creates a `Cursor` (holds reference to Connection for auth/base_url)
22
+ 3. `Cursor.execute(query)` → `_execute_request()` → HTTP POST to `/api/query` with retry
23
+ 4. Response is streamed: `ijson.parse()` extracts schema, then `ijson.items()` yields rows lazily
24
+ 5. `fetchone()`/`fetchall()`/`fetchmany()` consume from the row generator
25
+
26
+ ## Key Patterns
27
+
28
+ - **Retry logic** lives in `Cursor._execute_request()` — exponential backoff on 5xx/ConnectionError
29
+ - **Type conversion**: `util/types.py:convert_to_python_type()` maps API type names → Python types
30
+ - **Thread safety**: Connection uses `threading.local()`, Cursor uses `threading.Lock()`
31
+ - **No-ops**: `commit()`, `rollback()`, `setinputsizes()`, `setoutputsize()` are intentional no-ops
32
+ - **Exception hierarchy**: Must match PEP 249 exactly. `ConfigurationError` extends `Error` (custom addition)
33
+
34
+ ## Tests
35
+
36
+ ```bash
37
+ pip install -e ".[dev]"
38
+ pytest tests/unit/ -v # 140 tests, no server, ~0.1s
39
+ pytest tests/integration/ -v # 49 tests, mock server auto-starts, ~1s
40
+ ```
41
+
42
+ - **Unit**: `tests/unit/` — fixtures `mock_connection`/`mock_cursor` (mocked HTTP). Never hit network.
43
+ - **Integration**: `tests/integration/` — fixtures `con`/`cursor` (real HTTP to mock server).
44
+ - **Mock scenarios**: PAT-based. `any_token`=default, `error_pat`=401, `large_pat`=100+ rows. See `../connect-ai-mock/src/data/static/scenarios.json`.
45
+
46
+ ## Build
47
+
48
+ ```bash
49
+ pip install build && python -m build
50
+ # Produces dist/cdata_connect-1.0.0-py3-none-any.whl
51
+ ```
@@ -0,0 +1,315 @@
1
+ # cdata-connect Usage Examples
2
+
3
+ ## Installation
4
+
5
+ ```bash
6
+ pip install cdata-connect
7
+ ```
8
+
9
+ ## Basic Connection
10
+
11
+ ```python
12
+ import cdata_connect
13
+
14
+ conn = cdata_connect.connect(
15
+ base_url="https://cloud.cdata.com/api",
16
+ username="you@example.com",
17
+ password="<your_personal_access_token>",
18
+ )
19
+ ```
20
+
21
+ ## Using Context Managers (Recommended)
22
+
23
+ Connections and cursors support `with` statements for automatic cleanup:
24
+
25
+ ```python
26
+ import cdata_connect
27
+
28
+ with cdata_connect.connect(
29
+ base_url="https://cloud.cdata.com/api",
30
+ username="you@example.com",
31
+ password="<your_pat>",
32
+ ) as conn:
33
+ with conn.cursor() as cur:
34
+ cur.execute("SELECT * FROM [Salesforce1].[Salesforce].[Account]")
35
+ for row in cur:
36
+ print(row)
37
+ # Connection and cursor are automatically closed here
38
+ ```
39
+
40
+ ## Querying Data
41
+
42
+ ### Fetch All Rows
43
+
44
+ ```python
45
+ with conn.cursor() as cur:
46
+ cur.execute("SELECT Id, Name, Industry FROM [Salesforce1].[Salesforce].[Account]")
47
+ rows = cur.fetchall()
48
+ for row in rows:
49
+ print(f"Id={row[0]}, Name={row[1]}, Industry={row[2]}")
50
+ ```
51
+
52
+ ### Fetch One Row at a Time
53
+
54
+ ```python
55
+ with conn.cursor() as cur:
56
+ cur.execute("SELECT * FROM [Salesforce1].[Salesforce].[Contact]")
57
+ while True:
58
+ row = cur.fetchone()
59
+ if row is None:
60
+ break
61
+ print(row)
62
+ ```
63
+
64
+ ### Fetch in Batches
65
+
66
+ ```python
67
+ with conn.cursor() as cur:
68
+ cur.execute("SELECT * FROM [Salesforce1].[Salesforce].[Contact]")
69
+ while True:
70
+ batch = cur.fetchmany(100) # 100 rows at a time
71
+ if not batch:
72
+ break
73
+ for row in batch:
74
+ process(row)
75
+ ```
76
+
77
+ ### Iterate Directly (PEP 249 Iterator Protocol)
78
+
79
+ ```python
80
+ with conn.cursor() as cur:
81
+ cur.execute("SELECT Name, Email FROM [Salesforce1].[Salesforce].[Contact]")
82
+ for row in cur:
83
+ print(f"{row[0]} — {row[1]}")
84
+ ```
85
+
86
+ ## Column Metadata
87
+
88
+ ```python
89
+ with conn.cursor() as cur:
90
+ cur.execute("SELECT Id, Name, AnnualRevenue FROM [Salesforce1].[Salesforce].[Account]")
91
+
92
+ # cursor.description is a list of 7-tuples per PEP 249
93
+ for col in cur.description:
94
+ name, type_code, display_size, length, precision, scale, nullable = col
95
+ print(f"Column: {name}, Type: {type_code}")
96
+
97
+ rows = cur.fetchall()
98
+ ```
99
+
100
+ ## Parameterized Queries
101
+
102
+ The driver uses `pyformat` parameter style (`%(name)s` placeholders):
103
+
104
+ ```python
105
+ with conn.cursor() as cur:
106
+ cur.execute(
107
+ "SELECT * FROM [DB].[public].[users] WHERE city = %(city)s AND age > %(min_age)s",
108
+ {"city": "New York", "min_age": 25},
109
+ )
110
+ rows = cur.fetchall()
111
+ ```
112
+
113
+ ## Insert, Update, Delete
114
+
115
+ ```python
116
+ with conn.cursor() as cur:
117
+ # Insert
118
+ cur.execute("INSERT INTO [DB].[public].[users] (name, email) VALUES ('Alice', 'alice@example.com')")
119
+
120
+ # Update
121
+ cur.execute("UPDATE [DB].[public].[users] SET email = 'alice@new.com' WHERE name = 'Alice'")
122
+
123
+ # Delete
124
+ cur.execute("DELETE FROM [DB].[public].[users] WHERE name = 'Alice'")
125
+ ```
126
+
127
+ ## Batch Insert (executemany)
128
+
129
+ ```python
130
+ with conn.cursor() as cur:
131
+ cur.executemany(
132
+ "INSERT INTO [DB].[public].[cities] (city, id) VALUES (@city, @id)",
133
+ [
134
+ {"@city": {"dataType": 5, "value": "New York"}, "@id": {"dataType": 8, "value": 1}},
135
+ {"@city": {"dataType": 5, "value": "London"}, "@id": {"dataType": 8, "value": 2}},
136
+ {"@city": {"dataType": 5, "value": "Tokyo"}, "@id": {"dataType": 8, "value": 3}},
137
+ ],
138
+ )
139
+ ```
140
+
141
+ ## Stored Procedures
142
+
143
+ ```python
144
+ with conn.cursor() as cur:
145
+ result_args = cur.callproc("[DB].[public].[my_procedure]", ("arg1", "arg2"))
146
+ rows = cur.fetchall()
147
+ print(f"Input args returned: {result_args}")
148
+ print(f"Result rows: {rows}")
149
+ ```
150
+
151
+ ## Configuration File
152
+
153
+ Keep credentials out of code using a [PyHOCON](https://github.com/chimpler/pyhocon) config file:
154
+
155
+ ```hocon
156
+ # config.conf
157
+ cdata_api_db {
158
+ base_url = "https://cloud.cdata.com/api"
159
+ username = "you@example.com"
160
+ password = "<your_personal_access_token>"
161
+ }
162
+ ```
163
+
164
+ ```python
165
+ conn = cdata_connect.connect(config_path="config.conf")
166
+ ```
167
+
168
+ ## Workspaces
169
+
170
+ Access a specific CData Connect AI workspace:
171
+
172
+ ```python
173
+ conn = cdata_connect.connect(
174
+ base_url="https://cloud.cdata.com/api",
175
+ username="you@example.com",
176
+ password="<your_pat>",
177
+ workspace="production",
178
+ )
179
+ ```
180
+
181
+ ## Connection Options
182
+
183
+ ```python
184
+ conn = cdata_connect.connect(
185
+ base_url="https://cloud.cdata.com/api",
186
+ username="you@example.com",
187
+ password="<your_pat>",
188
+ timeout=60, # HTTP timeout in seconds (default: 30)
189
+ max_retries=5, # Retries on 5xx errors (default: 3)
190
+ retry_delay=2.0, # Base delay between retries in seconds (default: 1.0)
191
+ )
192
+ ```
193
+
194
+ ## Error Handling
195
+
196
+ ```python
197
+ import cdata_connect
198
+ from cdata_connect.exceptions import (
199
+ InterfaceError,
200
+ OperationalError,
201
+ ProgrammingError,
202
+ DatabaseError,
203
+ )
204
+
205
+ try:
206
+ conn = cdata_connect.connect(
207
+ base_url="https://cloud.cdata.com/api",
208
+ username="you@example.com",
209
+ password="<your_pat>",
210
+ )
211
+ with conn.cursor() as cur:
212
+ cur.execute("SELECT * FROM [Salesforce1].[Salesforce].[Account]")
213
+ rows = cur.fetchall()
214
+
215
+ except InterfaceError as e:
216
+ print(f"Connection/interface problem: {e}")
217
+
218
+ except OperationalError as e:
219
+ print(f"Server error, timeout, or connectivity issue: {e}")
220
+
221
+ except ProgrammingError as e:
222
+ print(f"Query error or fetch before execute: {e}")
223
+
224
+ except DatabaseError as e:
225
+ print(f"General database error: {e}")
226
+
227
+ finally:
228
+ if conn.is_open:
229
+ conn.close()
230
+ ```
231
+
232
+ ## Using with pandas
233
+
234
+ ```bash
235
+ pip install "cdata-connect[full]"
236
+ ```
237
+
238
+ ```python
239
+ import pandas as pd
240
+ import cdata_connect
241
+
242
+ conn = cdata_connect.connect(
243
+ base_url="https://cloud.cdata.com/api",
244
+ username="you@example.com",
245
+ password="<your_pat>",
246
+ )
247
+
248
+ with conn.cursor() as cur:
249
+ cur.execute("SELECT Id, Name, Industry, AnnualRevenue FROM [Salesforce1].[Salesforce].[Account]")
250
+ columns = [desc[0] for desc in cur.description]
251
+ rows = cur.fetchall()
252
+
253
+ df = pd.DataFrame(rows, columns=columns)
254
+ print(df.head())
255
+ print(df.describe())
256
+
257
+ conn.close()
258
+ ```
259
+
260
+ ## Type Objects
261
+
262
+ PEP 249 type objects for use with `cursor.description`:
263
+
264
+ ```python
265
+ import cdata_connect
266
+
267
+ # Check column types from cursor.description
268
+ with conn.cursor() as cur:
269
+ cur.execute("SELECT Id, Name, AnnualRevenue, CreatedDate FROM [Salesforce1].[Salesforce].[Account]")
270
+ for col in cur.description:
271
+ name, type_code = col[0], col[1]
272
+ if type_code == cdata_connect.STRING:
273
+ print(f"{name} is a string")
274
+ elif type_code == cdata_connect.NUMBER:
275
+ print(f"{name} is a number")
276
+ elif type_code == cdata_connect.DATETIME:
277
+ print(f"{name} is a datetime")
278
+ elif type_code == cdata_connect.BINARY:
279
+ print(f"{name} is binary")
280
+ ```
281
+
282
+ ## Date/Time Constructors
283
+
284
+ PEP 249 date/time helper constructors:
285
+
286
+ ```python
287
+ import cdata_connect
288
+
289
+ d = cdata_connect.Date(2025, 3, 15)
290
+ t = cdata_connect.Time(14, 30, 0)
291
+ ts = cdata_connect.Timestamp(2025, 3, 15, 14, 30, 0)
292
+ b = cdata_connect.Binary(b"raw bytes")
293
+
294
+ # From Unix timestamps
295
+ import time
296
+ d2 = cdata_connect.DateFromTicks(time.time())
297
+ t2 = cdata_connect.TimeFromTicks(time.time())
298
+ ts2 = cdata_connect.TimestampFromTicks(time.time())
299
+ ```
300
+
301
+ ## Exception Hierarchy
302
+
303
+ ```
304
+ cdata_connect.Warning — Important warnings (subclass of Exception)
305
+ cdata_connect.Error — Base error class
306
+ ├── InterfaceError — Connection/interface issues
307
+ ├── ConfigurationError — Bad config file or missing parameters
308
+ └── DatabaseError — Database-related errors
309
+ ├── DataError — Data processing errors (bad JSON, etc.)
310
+ ├── OperationalError — Server errors, timeouts, connectivity
311
+ ├── IntegrityError — Constraint violations
312
+ ├── InternalError — Internal database errors
313
+ ├── ProgrammingError — Query errors, fetch before execute
314
+ └── NotSupportedError — Unsupported operations
315
+ ```
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 CData Software, Inc.
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.
@@ -0,0 +1,8 @@
1
+ include README.md
2
+ include LICENSE
3
+ include pyproject.toml
4
+ prune dist
5
+ prune build
6
+ global-exclude *.py[cod]
7
+ global-exclude .DS_Store
8
+ global-exclude Thumbs.db