tracktolib 0.47.0__tar.gz → 0.47.2__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tracktolib
3
- Version: 0.47.0
3
+ Version: 0.47.2
4
4
  Summary: Utility library for python
5
5
  Home-page: https://github.com/tracktor/tracktolib
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "tracktolib"
3
- version = "0.47.0"
3
+ version = "0.47.2"
4
4
  description = "Utility library for python"
5
5
  authors = ["Julien Brayere <julien.brayere@tracktor.fr>"]
6
6
  license = "MIT"
@@ -70,7 +70,7 @@ pythonPlatform = "Linux"
70
70
 
71
71
  [tool.commitizen]
72
72
  name = "cz_conventional_commits"
73
- version = "0.47.0"
73
+ version = "0.47.2"
74
74
  tag_format = "$version"
75
75
  version_files = [
76
76
  "pyproject.toml:version"
@@ -20,7 +20,7 @@ extras_require = \
20
20
 
21
21
  setup_kwargs = {
22
22
  'name': 'tracktolib',
23
- 'version': '0.47.0',
23
+ 'version': '0.47.2',
24
24
  'description': 'Utility library for python',
25
25
  'long_description': "# Tracktolib\n\n[![Python versions](https://img.shields.io/pypi/pyversions/tracktolib)](https://pypi.python.org/pypi/tracktolib)\n[![Latest PyPI version](https://img.shields.io/pypi/v/tracktolib?logo=pypi)](https://pypi.python.org/pypi/tracktolib)\n[![CircleCI](https://circleci.com/gh/Tracktor/tracktolib/tree/master.svg?style=shield)](https://app.circleci.com/pipelines/github/Tracktor/tracktolib?branch=master)\n\nUtility library for python\n\n# Installation\n\nYou can choose to not install all the dependencies by specifying\nthe [extra](https://python-poetry.org/docs/cli/#options-4) parameter such as:\n\n```bash\npoetry add tracktolib@latest -E pg-sync -E tests --group dev \n```\n\nHere we only install the utilities using `psycopg` (pg-sync) and `deepdiff` (tests) for the dev environment.\n\n# Utilities\n\n- **log**\n\nUtility functions for logging.\n\n```python\nimport logging\nfrom tracktolib.logs import init_logging\n\nlogger = logging.getLogger()\nformatter, stream_handler = init_logging(logger, 'json', version='0.0.1')\n```\n\n- **pg**\n\nUtility functions for [asyncpg](https://github.com/MagicStack/asyncpg)\n\n- **pg-sync**\n\nUtility functions based on psycopg such as `fetch_one`, `insert_many`, `fetch_count` ...\n\nTo use the functions, create a `Connection` using psycopg: `conn = psycopg2.connect()`\n\n*fetch_one*\n\n```python\nfrom pg.pg_sync import (\n insert_many, fetch_one, fetch_count, fetch_all\n)\n\ndata = [\n {'foo': 'bar', 'value': 1},\n {'foo': 'baz', 'value': 2}\n]\ninsert_many(conn, 'public.test', data) # Will insert the 2 dict\nquery = 'SELECT foo from public.test order by value asc'\nvalue = fetch_one(conn, query, required=True) # Will return {'foo': 'bar'}, raise an error is not found\nassert fetch_count(conn, 'public.test') == 2\nquery = 'SELECT * from public.test order by value asc'\nassert fetch_all(conn, query) == data\n\n```\n\n- **tests**\n\nUtility functions for testing\n\n- **s3-minio**\n\nUtility functions for [minio](https://min.io/docs/minio/linux/developers/python/API.html)\n\n- **s3**\n\nUtility functions for [aiobotocore](https://github.com/aio-libs/aiobotocore)\n\n- **logs**\n\nUtility functions to initialize the logging formatting and streams\n\n- **http**\n\nUtility functions using [httpx](https://www.python-httpx.org/)\n\n- **api**\n\nUtility functions using [fastapi](https://fastapi.tiangolo.com/)\n",
26
26
  'author': 'Julien Brayere',
@@ -19,7 +19,7 @@ from typing import (
19
19
  get_origin,
20
20
  )
21
21
 
22
- from .utils import json_serial
22
+ from .utils import json_serial, get_first_line
23
23
 
24
24
  try:
25
25
  from fastapi import params, APIRouter
@@ -66,6 +66,9 @@ class MethodMeta(TypedDict):
66
66
  path: str | None
67
67
  response_model: Type[BaseModel | None | Sequence[BaseModel]] | None
68
68
  openapi_extra: dict[str, Any] | None
69
+ name: str | None
70
+ summary: str | None
71
+ description: str | None
69
72
 
70
73
 
71
74
  @dataclass
@@ -83,6 +86,9 @@ class Endpoint:
83
86
  path: str | None = None,
84
87
  model: Type[B] | None = None,
85
88
  openapi_extra: dict[str, Any] | None = None,
89
+ name: str | None = None,
90
+ summary: str | None = None,
91
+ description: str | None = None,
86
92
  ):
87
93
  return _get_method_wrapper(
88
94
  cls=self,
@@ -92,6 +98,9 @@ class Endpoint:
92
98
  path=path,
93
99
  model=model,
94
100
  openapi_extra=openapi_extra,
101
+ name=name,
102
+ summary=summary,
103
+ description=description,
95
104
  )
96
105
 
97
106
  def post(
@@ -102,6 +111,9 @@ class Endpoint:
102
111
  path: str | None = None,
103
112
  model: Type[B] | None = None,
104
113
  openapi_extra: dict[str, Any] | None = None,
114
+ name: str | None = None,
115
+ summary: str | None = None,
116
+ description: str | None = None,
105
117
  ):
106
118
  return _get_method_wrapper(
107
119
  cls=self,
@@ -111,6 +123,9 @@ class Endpoint:
111
123
  path=path,
112
124
  model=model,
113
125
  openapi_extra=openapi_extra,
126
+ name=name,
127
+ summary=summary,
128
+ description=description,
114
129
  )
115
130
 
116
131
  def put(
@@ -120,6 +135,9 @@ class Endpoint:
120
135
  path: str | None = None,
121
136
  model: Type[B] | None = None,
122
137
  openapi_extra: dict[str, Any] | None = None,
138
+ name: str | None = None,
139
+ summary: str | None = None,
140
+ description: str | None = None,
123
141
  ):
124
142
  return _get_method_wrapper(
125
143
  cls=self,
@@ -129,6 +147,9 @@ class Endpoint:
129
147
  path=path,
130
148
  model=model,
131
149
  openapi_extra=openapi_extra,
150
+ name=name,
151
+ summary=summary,
152
+ description=description,
132
153
  )
133
154
 
134
155
  def delete(
@@ -138,6 +159,9 @@ class Endpoint:
138
159
  path: str | None = None,
139
160
  model: Type[B] | None = None,
140
161
  openapi_extra: dict[str, Any] | None = None,
162
+ name: str | None = None,
163
+ summary: str | None = None,
164
+ description: str | None = None,
141
165
  ):
142
166
  return _get_method_wrapper(
143
167
  cls=self,
@@ -147,6 +171,9 @@ class Endpoint:
147
171
  path=path,
148
172
  model=model,
149
173
  openapi_extra=openapi_extra,
174
+ name=name,
175
+ summary=summary,
176
+ description=description,
150
177
  )
151
178
 
152
179
  def patch(
@@ -156,6 +183,9 @@ class Endpoint:
156
183
  path: str | None = None,
157
184
  model: Type[B] | None = None,
158
185
  openapi_extra: dict[str, Any] | None = None,
186
+ name: str | None = None,
187
+ summary: str | None = None,
188
+ description: str | None = None,
159
189
  ):
160
190
  return _get_method_wrapper(
161
191
  cls=self,
@@ -165,6 +195,9 @@ class Endpoint:
165
195
  path=path,
166
196
  model=model,
167
197
  openapi_extra=openapi_extra,
198
+ name=name,
199
+ summary=summary,
200
+ description=description,
168
201
  )
169
202
 
170
203
 
@@ -177,6 +210,9 @@ def _get_method_wrapper(
177
210
  path: str | None = None,
178
211
  model: Type[B] | None = None,
179
212
  openapi_extra: dict[str, Any] | None = None,
213
+ name: str | None = None,
214
+ summary: str | None = None,
215
+ description: str | None = None,
180
216
  ):
181
217
  def _set_method_wrapper(func: EnpointFn):
182
218
  if model is not None:
@@ -193,6 +229,9 @@ def _get_method_wrapper(
193
229
  "path": path,
194
230
  "response_model": model,
195
231
  "openapi_extra": _openapi_extra,
232
+ "name": name,
233
+ "summary": summary,
234
+ "description": description,
196
235
  }
197
236
  cls._methods[method] = _meta
198
237
 
@@ -223,6 +262,9 @@ def add_endpoint(
223
262
  _dependencies = _meta["dependencies"]
224
263
  _path = _meta["path"]
225
264
  _response_model = _meta["response_model"]
265
+ _name = _meta.get("name")
266
+ _summary = _meta.get("summary")
267
+ _description = _meta.get("description")
226
268
  if not _response_model:
227
269
  try:
228
270
  _response_model = _get_return_type(_fn)
@@ -231,15 +273,21 @@ def add_endpoint(
231
273
 
232
274
  full_path = path if not _path else f"{path}/{_path}"
233
275
 
234
- if not getdoc(_fn):
276
+ description = getdoc(_fn)
277
+ if not description:
235
278
  warnings.warn(f"Docstring is missing for {_method} {path}")
279
+ else:
280
+ _name = _name if _name else get_first_line(description)
281
+ _summary = _summary if _summary else get_first_line(description)
236
282
 
237
283
  # Todo: add warning name is not None
238
284
  router.add_api_route(
239
285
  full_path,
240
286
  _fn,
241
287
  methods=[_method],
242
- name=getdoc(_fn),
288
+ name=_name,
289
+ summary=_summary,
290
+ description=description,
243
291
  response_model=_response_model,
244
292
  status_code=_status_code,
245
293
  dependencies=[*(_dependencies or []), *(dependencies or [])],
@@ -13,7 +13,7 @@ try:
13
13
  import asyncpg
14
14
  from rich.progress import Progress
15
15
  except ImportError:
16
- raise ImportError('Please install tracktolib with "pg" to use this module')
16
+ raise ImportError('Please install asyncpg, rich or tracktolib with "pg" to use this module')
17
17
 
18
18
  from asyncpg.exceptions import (
19
19
  CheckViolationError,
@@ -126,9 +126,12 @@ def insert_csv(
126
126
  exclude_columns: Iterable[str] | None = None,
127
127
  delimiter: LiteralString = ",",
128
128
  block_size: int = 1000,
129
+ on_conflict: LiteralString = "ON CONFLICT DO NOTHING",
129
130
  ):
130
- _tmp_table, _tmp_query, _insert_query = get_tmp_table_query(schema, table)
131
- _columns = csv_path.open().readline()
131
+ _columns = cast(LiteralString, csv_path.open().readline())
132
+ _tmp_table, _tmp_query, _insert_query = get_tmp_table_query(
133
+ schema, table, columns=_columns.split(","), on_conflict=on_conflict
134
+ )
132
135
  _query: Query = query or cast(
133
136
  LiteralString,
134
137
  f"""
@@ -184,3 +184,8 @@ def deep_reload(m: ModuleType):
184
184
  sub_mods = [_mod for _mod in sys.modules if _mod == m.__name__ or _mod.startswith(f"{m.__name__}.")]
185
185
  for pkg in sorted(sub_mods, key=lambda item: item.count("."), reverse=True):
186
186
  importlib.reload(sys.modules[pkg])
187
+
188
+
189
+ def get_first_line(lines: str) -> str:
190
+ _lines = lines.split("\n")
191
+ return _lines[0] if _lines else lines
File without changes
File without changes