nlbone 0.6.12__py3-none-any.whl → 0.6.14__py3-none-any.whl

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.
@@ -36,7 +36,7 @@ class KeycloakAuthService(AuthService):
36
36
  realm_name=s.KEYCLOAK_REALM_NAME,
37
37
  client_secret_key=s.KEYCLOAK_CLIENT_SECRET.get_secret_value().strip(),
38
38
  )
39
- self.bypass = s.ENV != 'prod'
39
+ self.bypass = s.ENV != "prod"
40
40
 
41
41
  def has_access(self, token, permissions):
42
42
  if self.bypass:
@@ -145,4 +145,4 @@ class KeycloakAuthService(AuthService):
145
145
 
146
146
  @functools.lru_cache(maxsize=1)
147
147
  def get_auth_service() -> KeycloakAuthService:
148
- return KeycloakAuthService()
148
+ return KeycloakAuthService()
@@ -1,9 +1,11 @@
1
1
  from typing import Any, Callable, Optional, Sequence, Type, Union
2
2
 
3
- from sqlalchemy import asc, desc, or_, and_, case, literal
3
+ from sqlalchemy import and_, asc, case, desc, literal, or_
4
4
  from sqlalchemy.dialects.postgresql import ENUM as PGEnum
5
- from sqlalchemy.orm import Query, Session
5
+ from sqlalchemy.orm import Query, Session, aliased
6
+ from sqlalchemy.orm.attributes import InstrumentedAttribute
6
7
  from sqlalchemy.orm.interfaces import LoaderOption
8
+ from sqlalchemy.orm.relationships import RelationshipProperty
7
9
  from sqlalchemy.sql.sqltypes import (
8
10
  BigInteger,
9
11
  Boolean,
@@ -28,12 +30,6 @@ class _InvalidEnum(Exception):
28
30
  pass
29
31
 
30
32
 
31
- from sqlalchemy.orm import aliased
32
- from sqlalchemy.inspection import inspect
33
- from sqlalchemy.orm.attributes import InstrumentedAttribute
34
- from sqlalchemy.orm.relationships import RelationshipProperty
35
-
36
-
37
33
  def _resolve_column_and_joins(entity, query, field_path: str, join_cache: dict[str, Any]):
38
34
  parts = [p for p in field_path.split(".") if p]
39
35
  if not parts:
@@ -201,6 +197,7 @@ def _apply_filters(pagination, entity, query):
201
197
  return v
202
198
 
203
199
  try:
200
+
204
201
  def _use_ilike(v) -> bool:
205
202
  if op_hint == "ilike":
206
203
  return True
@@ -304,13 +301,13 @@ def _serialize_item(item: Any, output_cls: OutputType) -> Any:
304
301
 
305
302
 
306
303
  def get_paginated_response(
307
- pagination,
308
- entity,
309
- session: Session,
310
- *,
311
- with_count: bool = True,
312
- output_cls: Optional[Type] = None,
313
- eager_options: Optional[Sequence[LoaderOption]] = None,
304
+ pagination,
305
+ entity,
306
+ session: Session,
307
+ *,
308
+ with_count: bool = True,
309
+ output_cls: Optional[Type] = None,
310
+ eager_options: Optional[Sequence[LoaderOption]] = None,
314
311
  ) -> dict:
315
312
  query = session.query(entity)
316
313
  if eager_options:
@@ -35,11 +35,11 @@ def _filename_from_cd(cd: str | None, fallback: str) -> str:
35
35
 
36
36
  class UploadchiClient(FileServicePort):
37
37
  def __init__(
38
- self,
39
- token_provider: ClientTokenProvider | None = None,
40
- base_url: Optional[str] = None,
41
- timeout_seconds: Optional[float] = None,
42
- client: httpx.Client | None = None,
38
+ self,
39
+ token_provider: ClientTokenProvider | None = None,
40
+ base_url: Optional[str] = None,
41
+ timeout_seconds: Optional[float] = None,
42
+ client: httpx.Client | None = None,
43
43
  ) -> None:
44
44
  s = get_settings()
45
45
  self._base_url = normalize_https_base(base_url or str(s.UPLOADCHI_BASE_URL))
@@ -51,7 +51,7 @@ class UploadchiClient(FileServicePort):
51
51
  self._client.close()
52
52
 
53
53
  def upload_file(
54
- self, file_bytes: bytes, filename: str, params: dict[str, Any] | None = None, token: str | None = None
54
+ self, file_bytes: bytes, filename: str, params: dict[str, Any] | None = None, token: str | None = None
55
55
  ) -> dict:
56
56
  tok = _resolve_token(token)
57
57
  files = {"file": (filename, file_bytes)}
@@ -84,12 +84,12 @@ class UploadchiClient(FileServicePort):
84
84
  raise UploadchiError(r.status_code, r.text)
85
85
 
86
86
  def list_files(
87
- self,
88
- limit: int = 10,
89
- offset: int = 0,
90
- filters: dict[str, Any] | None = None,
91
- sort: list[tuple[str, str]] | None = None,
92
- token: str | None = None,
87
+ self,
88
+ limit: int = 10,
89
+ offset: int = 0,
90
+ filters: dict[str, Any] | None = None,
91
+ sort: list[tuple[str, str]] | None = None,
92
+ token: str | None = None,
93
93
  ) -> dict:
94
94
  tok = _resolve_token(token)
95
95
  q = build_list_query(limit, offset, filters, sort)
@@ -116,7 +116,8 @@ class UploadchiClient(FileServicePort):
116
116
 
117
117
  def delete_file(self, file_id: str, token: str | None = None) -> None:
118
118
  tok = _resolve_token(token)
119
- r = self._client.delete(f"{self._base_url}/{file_id}",
120
- headers=auth_headers(tok or self._token_provider.get_access_token()))
119
+ r = self._client.delete(
120
+ f"{self._base_url}/{file_id}", headers=auth_headers(tok or self._token_provider.get_access_token())
121
+ )
121
122
  if r.status_code not in (204, 200):
122
123
  raise UploadchiError(r.status_code, r.text)
@@ -9,6 +9,8 @@ def get_es_client():
9
9
  es = Elasticsearch(
10
10
  setting.ELASTIC_PERCOLATE_URL,
11
11
  basic_auth=(setting.ELASTIC_PERCOLATE_USER, setting.ELASTIC_PERCOLATE_PASS.get_secret_value().strip()),
12
- http_compress=True, max_retries=2, retry_on_timeout=True
12
+ http_compress=True,
13
+ max_retries=2,
14
+ retry_on_timeout=True,
13
15
  )
14
16
  return es
nlbone/container.py CHANGED
@@ -1,8 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import Any, Mapping, Optional
3
+ from typing import Dict, Optional
4
4
 
5
5
  from dependency_injector import containers, providers
6
+ from pydantic_settings import BaseSettings
6
7
 
7
8
  from nlbone.adapters.auth.keycloak import KeycloakAuthService
8
9
  from nlbone.adapters.auth.token_provider import ClientTokenProvider
@@ -15,7 +16,6 @@ from nlbone.adapters.http_clients import PricingService
15
16
  from nlbone.adapters.http_clients.uploadchi import UploadchiClient
16
17
  from nlbone.adapters.http_clients.uploadchi.uploadchi_async import UploadchiAsyncClient
17
18
  from nlbone.adapters.messaging import InMemoryEventBus
18
- from nlbone.config.settings import Settings
19
19
  from nlbone.core.ports import EventBusPort
20
20
  from nlbone.core.ports.cache import AsyncCachePort, CachePort
21
21
  from nlbone.core.ports.files import AsyncFileServicePort, FileServicePort
@@ -35,7 +35,7 @@ class Container(containers.DeclarativeContainer):
35
35
  event_bus: providers.Singleton[EventBusPort] = providers.Singleton(InMemoryEventBus)
36
36
 
37
37
  # --- Services ---
38
- auth: providers.Singleton[KeycloakAuthService] = providers.Singleton(KeycloakAuthService, settings=config)
38
+ auth: providers.Singleton[KeycloakAuthService] = providers.Singleton(KeycloakAuthService)
39
39
  token_provider = providers.Singleton(ClientTokenProvider, auth=auth, skew_seconds=30)
40
40
  file_service: providers.Singleton[FileServicePort] = providers.Singleton(
41
41
  UploadchiClient, token_provider=token_provider
@@ -60,15 +60,11 @@ class Container(containers.DeclarativeContainer):
60
60
  )
61
61
 
62
62
 
63
- def create_container(settings: Optional[Any] = None) -> Container:
63
+ def create_container(settings: Optional[BaseSettings | Dict] = None) -> Container:
64
64
  c = Container()
65
65
  if settings is not None:
66
- if isinstance(settings, Settings):
67
- c.config.override(settings)
68
- elif hasattr(settings, "model_dump"):
69
- c.config.from_dict(settings.model_dump()) # Pydantic v2
70
- elif hasattr(settings, "dict"):
71
- c.config.from_dict(settings.dict()) # Pydantic v1
72
- elif isinstance(settings, Mapping):
73
- c.config.from_dict(dict(settings))
66
+ if isinstance(settings, BaseSettings):
67
+ c.config.from_pydantic(settings)
68
+ elif isinstance(settings, Dict):
69
+ c.config.from_dict(settings)
74
70
  return c
@@ -1,6 +1,6 @@
1
1
  import json
2
2
  from math import ceil
3
- from typing import Any, Optional, List
3
+ from typing import Any, List, Optional
4
4
 
5
5
  from fastapi import Query
6
6
 
@@ -13,18 +13,20 @@ class PaginateRequest:
13
13
  """
14
14
 
15
15
  def __init__(
16
- self,
17
- limit: int = 10,
18
- offset: int = 0,
19
- sort: Optional[str] = None,
20
- filters: Optional[str] = Query(None, description="e.g. title:abc"),
21
- include: Optional[str] = None,
16
+ self,
17
+ limit: int = 10,
18
+ offset: int = 0,
19
+ sort: Optional[str] = None,
20
+ filters: Optional[str] = Query(None, description="e.g. title:abc"),
21
+ include: Optional[str] = None,
22
22
  ) -> None:
23
23
  self.limit = max(0, limit)
24
24
  self.offset = max(0, offset)
25
25
  self.sort = self._parse_sort(sort)
26
26
  self.filters = self._parse_filters(filters or "")
27
- self.include_ids: List[int] = ([int(x) for x in include.split(",") if x.strip().isdigit()] if include else [])[:50]
27
+ self.include_ids: List[int] = ([int(x) for x in include.split(",") if x.strip().isdigit()] if include else [])[
28
+ :50
29
+ ]
28
30
 
29
31
  @staticmethod
30
32
  def _parse_sort(sort_str: Optional[str]) -> list[dict[str, str]]:
@@ -83,12 +85,12 @@ class PaginateResponse:
83
85
  """
84
86
 
85
87
  def __init__(
86
- self,
87
- data: list[Any],
88
- total_count: int | None,
89
- limit: int,
90
- offset: int,
91
- use_data_key: bool = True,
88
+ self,
89
+ data: list[Any],
90
+ total_count: int | None,
91
+ limit: int,
92
+ offset: int,
93
+ use_data_key: bool = True,
92
94
  ) -> None:
93
95
  self.data = data
94
96
  self.total_count = total_count
@@ -0,0 +1,2 @@
1
+ from .adaptive_schema import AdaptiveSchemaBase, ResponsePreference
2
+ from .base_response_model import BaseResponseModel
@@ -0,0 +1,78 @@
1
+ from abc import ABC, abstractmethod
2
+ from enum import Enum
3
+ from typing import Any, List, Type, get_args, get_origin
4
+
5
+ from pydantic import BaseModel
6
+
7
+ from nlbone.utils.context import current_request
8
+
9
+
10
+ class ResponsePreference(str, Enum):
11
+ minimal = "minimal"
12
+ lite = "lite"
13
+ full = "full"
14
+
15
+
16
+ class AdaptiveSchemaBase(ABC):
17
+ @classmethod
18
+ @abstractmethod
19
+ def minimal(cls) -> Type:
20
+ pass
21
+
22
+ @classmethod
23
+ @abstractmethod
24
+ def lite(cls) -> Type:
25
+ pass
26
+
27
+ @classmethod
28
+ @abstractmethod
29
+ def full(cls) -> Type:
30
+ pass
31
+
32
+ @classmethod
33
+ def choose(cls, preference: ResponsePreference) -> Type:
34
+ if preference == ResponsePreference.minimal:
35
+ return cls.minimal()
36
+ elif preference == ResponsePreference.lite:
37
+ return cls.lite()
38
+ elif preference == ResponsePreference.full:
39
+ return cls.full()
40
+ return cls.lite()
41
+
42
+ @classmethod
43
+ def serialize(cls, obj: Any, preference: ResponsePreference = None) -> Any:
44
+ if not preference:
45
+ preference = current_request().state.response_preference
46
+
47
+ schema = cls.choose(preference)
48
+
49
+ if isinstance(obj, list):
50
+ return [cls._recursive_serialize(schema, item) for item in obj]
51
+
52
+ return cls._recursive_serialize(schema, obj)
53
+
54
+ @classmethod
55
+ def _recursive_serialize(cls, schema_cls: type[BaseModel], item: Any) -> dict:
56
+ # Create base dict from ORM model
57
+ raw_data = schema_cls.model_validate(item).model_dump()
58
+
59
+ # Check each field to see if it needs nested serialization
60
+ annotations = schema_cls.__annotations__
61
+
62
+ for field_name, field_type in annotations.items():
63
+ field_value = getattr(item, field_name, None)
64
+
65
+ if field_value is None:
66
+ continue
67
+
68
+ # Check for nested AdaptiveSchemaBase (single object)
69
+ if isinstance(field_type, type) and issubclass(field_type, AdaptiveSchemaBase):
70
+ raw_data[field_name] = field_type.serialize(field_value)
71
+
72
+ # Check for list[AdaptiveSchemaBase]
73
+ elif get_origin(field_type) in (list, List):
74
+ sub_type = get_args(field_type)[0]
75
+ if isinstance(sub_type, type) and issubclass(sub_type, AdaptiveSchemaBase):
76
+ raw_data[field_name] = sub_type.serialize(field_value)
77
+
78
+ return raw_data
@@ -0,0 +1,5 @@
1
+ from pydantic import BaseModel
2
+
3
+
4
+ class BaseResponseModel(BaseModel):
5
+ pass
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nlbone
3
- Version: 0.6.12
3
+ Version: 0.6.14
4
4
  Summary: Backbone package for interfaces and infrastructure in Python projects
5
5
  Author-email: Amir Hosein Kahkbazzadeh <a.khakbazzadeh@gmail.com>
6
6
  License: MIT
@@ -1,9 +1,9 @@
1
1
  nlbone/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- nlbone/container.py,sha256=VPGkfQO0HS4SbQy2ja3HVKyuMjD94p5ZmDPSqYHGhNo,3317
2
+ nlbone/container.py,sha256=rVYzH-jIM8iCcefDOo29mNjvFdf3nJ4EtPNUws9SDnA,3089
3
3
  nlbone/types.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  nlbone/adapters/__init__.py,sha256=NzUmk4XPyp3GJOw7VSE86xkQMZLtG3MrOoXLeoB551M,41
5
5
  nlbone/adapters/auth/__init__.py,sha256=hkDHvsFhw_UiOHG9ZSMqjiAhK4wumEforitveSZswVw,42
6
- nlbone/adapters/auth/keycloak.py,sha256=ZGWeq9by2onb440kNRm4BdcxnO7N1qDPmg8UUrXrUrQ,4924
6
+ nlbone/adapters/auth/keycloak.py,sha256=IhEriaFl5mjIGT6ZUCU9qROd678ARchvWgd4UJ6zH7s,4925
7
7
  nlbone/adapters/auth/token_provider.py,sha256=vL2Hk6HXnBbpk40Tq1wpqak5QQ7KEQf3nRquT0N8V4Q,1433
8
8
  nlbone/adapters/cache/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
9
  nlbone/adapters/cache/async_redis.py,sha256=vvu5w4ANx0BVRHL95RAMGsD8CcaC-tSBMbCius2cuNc,6212
@@ -15,7 +15,7 @@ nlbone/adapters/db/postgres/__init__.py,sha256=6JYJH0xZs3aR-zuyMpRhsdzFugmqz8npr
15
15
  nlbone/adapters/db/postgres/audit.py,sha256=8f5XOuW7_ybJyy_STam1FNzqmZAAVAu7tmMRUkCGJOM,4594
16
16
  nlbone/adapters/db/postgres/base.py,sha256=kha9xmklzhuQAK8QEkNBn-mAHq8dUKbOM-3abaBpWmQ,71
17
17
  nlbone/adapters/db/postgres/engine.py,sha256=UCegauVB1gvo42ThytYnn5VIcQBwR-5xhcXYFApRFNk,3448
18
- nlbone/adapters/db/postgres/query_builder.py,sha256=SNNkmoMuzcuOwxZlTlplAGqJ0mDQevixLOruqHPa8zM,10855
18
+ nlbone/adapters/db/postgres/query_builder.py,sha256=yZ5c7avl_NoMkybmV9vdoEuyUOVxuefuRxYqJq383S4,10758
19
19
  nlbone/adapters/db/postgres/repository.py,sha256=J_DBE73JhHPYCk90c5-O7lQtZbxDgqjjN9OcWy4Omvs,1660
20
20
  nlbone/adapters/db/postgres/schema.py,sha256=NlE7Rr8uXypsw4oWkdZhZwcIBHQEPIpoHLxcUo98i6s,1039
21
21
  nlbone/adapters/db/postgres/uow.py,sha256=nRxNpY-WoWHpym-XeZ8VHm0MYvtB9wuopOeNdV_ebk8,2088
@@ -25,13 +25,13 @@ nlbone/adapters/http_clients/__init__.py,sha256=w-Yr9CLuXMU71N0Ada5HbvP1DB53wqeP
25
25
  nlbone/adapters/http_clients/pricing/__init__.py,sha256=ElA9NFcAR9u4cqb_w3PPqKU3xGeyjNLQ8veJ0ql2iz0,81
26
26
  nlbone/adapters/http_clients/pricing/pricing_service.py,sha256=PDG6CbLg_SL-BsrhNwfyypywcuZIsEyj5mpQGSPH4e4,3300
27
27
  nlbone/adapters/http_clients/uploadchi/__init__.py,sha256=uBzEOuVtY22teWW2b36Pitkdk5yVdSqa6xbg22JfTNg,105
28
- nlbone/adapters/http_clients/uploadchi/uploadchi.py,sha256=oOkjDA7MMGe7HNl7qgoPbeV_EI5PNIx1yidsxvnkhis,4939
28
+ nlbone/adapters/http_clients/uploadchi/uploadchi.py,sha256=erpjOees25FW0nuK1PkYS-oU0h8MeRV9Rhs1cf3gaEs,4881
29
29
  nlbone/adapters/http_clients/uploadchi/uploadchi_async.py,sha256=PQbVNeaYde5CmgT3vcnQoI1PGeSs9AxHlPFuB8biOmU,4717
30
30
  nlbone/adapters/messaging/__init__.py,sha256=UDAwu3s-JQmOZjWz2Nu0SgHhnkbeOhKDH_zLD75oWMY,40
31
31
  nlbone/adapters/messaging/event_bus.py,sha256=w-NPwDiPMLFPU_enRQCtfQXOALsXfg31u57R8sG_-1U,781
32
32
  nlbone/adapters/messaging/redis.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
33
  nlbone/adapters/percolation/__init__.py,sha256=0h1Bw7FzxgkDIHxeoyQXSfegrhP6VbpYV4QC8njYdRE,38
34
- nlbone/adapters/percolation/connection.py,sha256=KUlYPFBXyjv_IEt8zgwdNKynl4VnzL7bp-hcll48Z2w,398
34
+ nlbone/adapters/percolation/connection.py,sha256=1iJISSwMEh4r_6nJI7mYf_v64Q0eeU1eSI0wLIfOK14,415
35
35
  nlbone/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
36
  nlbone/config/logging.py,sha256=Ot6Ctf7EQZlW8YNB-uBdleqI6wixn5fH0Eo6QRgNkQk,4358
37
37
  nlbone/config/settings.py,sha256=W3NHZP6yjIyyKiGWNkjlUt_RYFKkcIfMBoKih_z_0Bs,3911
@@ -70,7 +70,10 @@ nlbone/interfaces/api/middleware/access_log.py,sha256=vIkxxxfy2HcjqqKb8XCfGCcSri
70
70
  nlbone/interfaces/api/middleware/add_request_context.py,sha256=av-qs0biOYuF9R6RJOo2eYsFqDL9WRYWcjVakFhbt-w,1834
71
71
  nlbone/interfaces/api/middleware/authentication.py,sha256=ze7vCm492QsX9nPL6A-PqZCmC1C5ZgUE-OWI6fCLpsU,1809
72
72
  nlbone/interfaces/api/pagination/__init__.py,sha256=sWKKQFa2Z-1SlprQOqImOa2c9exq4wueKpUL_9QM7wc,417
73
- nlbone/interfaces/api/pagination/offset_base.py,sha256=BPG8veDRUA-67Z_JqLQTqT2nqCalxPblzSiQv_HZXUc,3838
73
+ nlbone/interfaces/api/pagination/offset_base.py,sha256=iipO09sAUb0U9ctMMujBHQo1IgMRbasvwnHJFtA9ZeA,3812
74
+ nlbone/interfaces/api/schema/__init__.py,sha256=LAqgynfupeqOQ6u0I5ucrcYnojRMZUg9yW8IjKSQTNI,119
75
+ nlbone/interfaces/api/schema/adaptive_schema.py,sha256=JeD3NnueYspBA5oYcLyKOMM0YuuWvCqvrow6oGIODMs,2406
76
+ nlbone/interfaces/api/schema/base_response_model.py,sha256=ZVlJwiZFD8abOZeKZuiR_LIrIvEGmBpXaaNp2omohlo,78
74
77
  nlbone/interfaces/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
75
78
  nlbone/interfaces/cli/init_db.py,sha256=C67n2MsJ1vzkJxC8zfUYOxFdd6mEB_vT9agxN6jWoG8,790
76
79
  nlbone/interfaces/cli/main.py,sha256=pNldsTgplVyXa-Hx96dySO2I9gFRi49nDXv7J_dO73s,686
@@ -84,8 +87,8 @@ nlbone/utils/context.py,sha256=MmclJ24BG2uvSTg1IK7J-Da9BhVFDQ5ag4Ggs2FF1_w,1600
84
87
  nlbone/utils/http.py,sha256=UXUoXgQdTRNT08ho8zl-C5ekfDsD8uf-JiMQ323ooqw,872
85
88
  nlbone/utils/redactor.py,sha256=-V4HrHmHwPi3Kez587Ek1uJlgK35qGSrwBOvcbw8Jas,1279
86
89
  nlbone/utils/time.py,sha256=DjjyQ9GLsfXoT6NK8RDW2rOlJg3e6sF04Jw6PBUrSvg,1268
87
- nlbone-0.6.12.dist-info/METADATA,sha256=l0a2XnCXdUv6VvOv_p880moTJFSwVLzLCC6L8NtapMg,2228
88
- nlbone-0.6.12.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
89
- nlbone-0.6.12.dist-info/entry_points.txt,sha256=CpIL45t5nbhl1dGQPhfIIDfqqak3teK0SxPGBBr7YCk,59
90
- nlbone-0.6.12.dist-info/licenses/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
91
- nlbone-0.6.12.dist-info/RECORD,,
90
+ nlbone-0.6.14.dist-info/METADATA,sha256=h6LA8odMlLRi3mK5sEgNdy8DguwNa4QOADuQ9rGT3Xg,2228
91
+ nlbone-0.6.14.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
92
+ nlbone-0.6.14.dist-info/entry_points.txt,sha256=CpIL45t5nbhl1dGQPhfIIDfqqak3teK0SxPGBBr7YCk,59
93
+ nlbone-0.6.14.dist-info/licenses/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
94
+ nlbone-0.6.14.dist-info/RECORD,,