sqlobjects 1.8.0__py3-none-any.whl → 1.9.1__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.
Files changed (18) hide show
  1. sqlobjects/__init__.py +2 -2
  2. sqlobjects/queries/builder.py +3 -3
  3. sqlobjects/queries/executor.py +1 -2
  4. sqlobjects/sql_logging.py +118 -74
  5. {sqlobjects-1.8.0.dist-info → sqlobjects-1.9.1.dist-info}/METADATA +1 -1
  6. {sqlobjects-1.8.0.dist-info → sqlobjects-1.9.1.dist-info}/RECORD +18 -18
  7. {sqlobjects-1.8.0.data → sqlobjects-1.9.1.data}/data/share/sqlobjects/rules/01-database-session-guide.md +0 -0
  8. {sqlobjects-1.8.0.data → sqlobjects-1.9.1.data}/data/share/sqlobjects/rules/02-model-definition-guide.md +0 -0
  9. {sqlobjects-1.8.0.data → sqlobjects-1.9.1.data}/data/share/sqlobjects/rules/03-query-operations-guide.md +0 -0
  10. {sqlobjects-1.8.0.data → sqlobjects-1.9.1.data}/data/share/sqlobjects/rules/04-crud-operations-guide.md +0 -0
  11. {sqlobjects-1.8.0.data → sqlobjects-1.9.1.data}/data/share/sqlobjects/rules/05-relationships-guide.md +0 -0
  12. {sqlobjects-1.8.0.data → sqlobjects-1.9.1.data}/data/share/sqlobjects/rules/06-validation-signals-guide.md +0 -0
  13. {sqlobjects-1.8.0.data → sqlobjects-1.9.1.data}/data/share/sqlobjects/rules/07-performance-guide.md +0 -0
  14. {sqlobjects-1.8.0.data → sqlobjects-1.9.1.data}/data/share/sqlobjects/rules/README.md +0 -0
  15. {sqlobjects-1.8.0.dist-info → sqlobjects-1.9.1.dist-info}/WHEEL +0 -0
  16. {sqlobjects-1.8.0.dist-info → sqlobjects-1.9.1.dist-info}/entry_points.txt +0 -0
  17. {sqlobjects-1.8.0.dist-info → sqlobjects-1.9.1.dist-info}/licenses/LICENSE +0 -0
  18. {sqlobjects-1.8.0.dist-info → sqlobjects-1.9.1.dist-info}/top_level.txt +0 -0
sqlobjects/__init__.py CHANGED
@@ -16,7 +16,7 @@ from .objects import (
16
16
  TransactionMode,
17
17
  )
18
18
  from .queryset import Q, QuerySet
19
- from .sql_logging import SQLCallerFilter, get_caller_frame
19
+ from .sql_logging import ObjectLogger, get_caller_frame
20
20
 
21
21
 
22
22
  __version__ = "0.3.0"
@@ -43,6 +43,6 @@ __all__ = [
43
43
  "ErrorHandling",
44
44
  "ConflictResolution",
45
45
  # SQL logging
46
- "SQLCallerFilter",
46
+ "ObjectLogger",
47
47
  "get_caller_frame",
48
48
  ]
@@ -70,16 +70,16 @@ class QueryBuilder:
70
70
  return new_builder
71
71
 
72
72
  def add_ordering(self, *fields):
73
- """Add ORDER BY fields to the query.
73
+ """Set ORDER BY fields for the query, replacing any existing ordering.
74
74
 
75
75
  Args:
76
76
  *fields: Field names or SQLAlchemy ordering expressions
77
77
 
78
78
  Returns:
79
- New QueryBuilder instance with added ordering
79
+ New QueryBuilder instance with replaced ordering
80
80
  """
81
81
  new_builder = self.copy()
82
- new_builder.ordering.extend(fields)
82
+ new_builder.ordering = list(fields)
83
83
  return new_builder
84
84
 
85
85
  def add_limit(self, count: int):
@@ -14,8 +14,7 @@ from sqlalchemy import (
14
14
  update,
15
15
  )
16
16
 
17
-
18
- _sql_logger = logging.getLogger("sqlobjects.sql")
17
+ from ..sql_logging import _sql_logger
19
18
 
20
19
 
21
20
  _T = TypeVar("_T")
sqlobjects/sql_logging.py CHANGED
@@ -1,9 +1,9 @@
1
1
  """SQL logging utilities for sqlobjects.
2
2
 
3
- Provides get_caller_frame() to surface user-code caller information in SQL log
4
- records, compatible with standard logging and loguru.
3
+ Provides ObjectLogger (a logging.Logger subclass that auto-rewrites caller
4
+ fields to user-code location) and get_caller_frame() for custom scenarios.
5
5
 
6
- Filter strategy:
6
+ Frame-skip strategy:
7
7
  - Skips frames from site-packages (covers pip-installed sqlobjects/sqlalchemy)
8
8
  - Skips frames whose module name starts with "sqlobjects." or "sqlalchemy."
9
9
  (covers editable installs via `pip install -e .`)
@@ -15,12 +15,14 @@ from __future__ import annotations
15
15
  import inspect
16
16
  import logging
17
17
  import os
18
+ from collections.abc import Mapping
19
+ from typing import Any
18
20
 
19
21
 
20
- __all__ = ["get_caller_frame", "SQLCallerFilter"]
22
+ __all__ = ["get_caller_frame", "ObjectLogger"]
21
23
 
22
24
  # Exact module names that are always considered internal
23
- _INTERNAL_MODULES = {"sqlobjects", "sqlalchemy"}
25
+ _INTERNAL_MODULES = {"sqlobjects", "sqlalchemy", "logging"}
24
26
 
25
27
  # Module name prefixes that are always considered internal
26
28
  _INTERNAL_PREFIXES = ("sqlobjects.", "sqlalchemy.")
@@ -29,6 +31,48 @@ _INTERNAL_PREFIXES = ("sqlobjects.", "sqlalchemy.")
29
31
  _THIS_FILE = os.path.abspath(__file__)
30
32
 
31
33
 
34
+ def _should_skip_frame(
35
+ filepath: str,
36
+ module: str,
37
+ extra_skip_prefixes: tuple[str, ...],
38
+ ) -> bool:
39
+ """Return True if this frame should be skipped (library/internal frame).
40
+
41
+ Args:
42
+ filepath: frame_info.filename
43
+ module: frame.f_globals.get("__name__", "")
44
+ extra_skip_prefixes: tuple of module name prefixes to skip in addition
45
+ to the built-in sqlobjects/sqlalchemy prefixes.
46
+ """
47
+ if "site-packages" in filepath:
48
+ return True
49
+ if filepath.startswith("<"):
50
+ return True
51
+ if module in _INTERNAL_MODULES or module.startswith(_INTERNAL_PREFIXES):
52
+ return True
53
+ if filepath == _THIS_FILE or os.path.abspath(filepath) == _THIS_FILE:
54
+ return True
55
+ if extra_skip_prefixes and module.startswith(extra_skip_prefixes):
56
+ return True
57
+ return False
58
+
59
+
60
+ def _find_user_frame(
61
+ extra_skip_packages: list[str] | None = None,
62
+ ) -> inspect.FrameInfo | None:
63
+ """Return the first user-code FrameInfo, skipping library frames.
64
+
65
+ Applies the same skip rules as get_caller_frame() but returns the raw
66
+ FrameInfo instead of a formatted string. Returns None if no frame found.
67
+ """
68
+ extra_skip_prefixes: tuple[str, ...] = tuple(extra_skip_packages) if extra_skip_packages else ()
69
+ for frame_info in inspect.stack():
70
+ module = frame_info.frame.f_globals.get("__name__", "")
71
+ if not _should_skip_frame(frame_info.filename, module, extra_skip_prefixes):
72
+ return frame_info
73
+ return None
74
+
75
+
32
76
  def get_caller_frame(
33
77
  extra_skip_packages: list[str] | None = None,
34
78
  max_frames: int = 1,
@@ -50,37 +94,20 @@ def get_caller_frame(
50
94
  Frame string "path/to/file.py:lineno in funcname", or a list of such
51
95
  strings when max_frames > 1.
52
96
  """
53
- skip_prefixes = _INTERNAL_PREFIXES
54
- if extra_skip_packages:
55
- skip_prefixes = skip_prefixes + tuple(extra_skip_packages)
56
-
97
+ extra_skip_prefixes: tuple[str, ...] = tuple(extra_skip_packages) if extra_skip_packages else ()
57
98
  frames: list[str] = []
58
99
 
59
100
  for frame_info in inspect.stack():
60
- filepath = frame_info.filename
61
101
  module = frame_info.frame.f_globals.get("__name__", "")
62
-
63
- # Skip site-packages frames (pip-installed third-party libs)
64
- if "site-packages" in filepath:
65
- continue
66
-
67
- # Skip sqlobjects/sqlalchemy frames (editable install)
68
- if module in _INTERNAL_MODULES or module.startswith(skip_prefixes):
69
- continue
70
-
71
- # Skip this helper file itself
72
- if os.path.abspath(filepath) == _THIS_FILE:
102
+ if _should_skip_frame(frame_info.filename, module, extra_skip_prefixes):
73
103
  continue
74
-
75
- rel_path = _relative_path(filepath)
104
+ rel_path = _relative_path(frame_info.filename)
76
105
  frames.append(f"{rel_path}:{frame_info.lineno} in {frame_info.function}")
77
-
78
106
  if len(frames) >= max_frames:
79
107
  break
80
108
 
81
109
  if not frames:
82
110
  return "<unknown>" if max_frames == 1 else ["<unknown>"]
83
-
84
111
  return frames[0] if max_frames == 1 else frames
85
112
 
86
113
 
@@ -92,65 +119,82 @@ def _relative_path(filepath: str) -> str:
92
119
  return filepath
93
120
 
94
121
 
95
- class SQLCallerFilter(logging.Filter):
96
- """logging.Filter that rewrites LogRecord caller fields to user-code location.
97
-
98
- Inspects the call stack at filter time, skips library frames (site-packages
99
- and sqlobjects.*/sqlalchemy.* modules for editable installs), and overwrites
100
- record.filename / record.funcName / record.lineno / record.pathname so that
101
- any handler (including loguru interception) displays the real user-code
102
- call site.
122
+ class ObjectLogger(logging.Logger):
123
+ """logging.Logger subclass that rewrites LogRecord caller fields to user-code location.
103
124
 
104
- Also exposes record.caller (str or list[str]) for use in custom Formatters.
125
+ Overrides makeRecord() to replace the standard caller fields
126
+ (filename, funcName, lineno, pathname) with the first user-code frame
127
+ found by _find_user_frame(). This means any handler attached to this logger
128
+ — including loguru InterceptHandlers — will display the real call site
129
+ without any additional Filter configuration.
105
130
 
106
131
  Args:
107
- max_frames: Number of user-code frames to capture (default 1).
108
- When 1, record.caller is a str and record location fields point
109
- to that single frame.
110
- When > 1, record.caller is a list[str] and record location fields
111
- are set from the first (most recent) frame.
112
- extra_skip_packages: Additional module name prefixes to skip
113
- (matched against frame's __name__).
132
+ name: Logger name (passed to logging.Logger).
133
+ level: Initial log level (default NOTSET).
134
+ extra_skip_packages: Additional module name prefixes to skip when
135
+ searching for the user-code frame (e.g. ["myapp.middleware"]).
114
136
  """
115
137
 
116
138
  def __init__(
117
139
  self,
118
- max_frames: int = 1,
140
+ name: str,
141
+ level: int = logging.NOTSET,
119
142
  extra_skip_packages: list[str] | None = None,
120
143
  ) -> None:
121
- super().__init__()
122
- self.max_frames = max_frames
123
- self.extra_skip_packages: list[str] | None = list(extra_skip_packages) if extra_skip_packages else None
144
+ super().__init__(name, level)
145
+ self.extra_skip_packages = extra_skip_packages
124
146
 
125
- def filter(self, record: logging.LogRecord) -> bool:
126
- caller = get_caller_frame(
127
- extra_skip_packages=self.extra_skip_packages,
128
- max_frames=self.max_frames,
129
- )
130
- record.caller = caller
147
+ def makeRecord( # noqa: N802
148
+ self,
149
+ name: str,
150
+ level: int,
151
+ fn: str,
152
+ lno: int,
153
+ msg: object,
154
+ args: Any,
155
+ exc_info: Any,
156
+ func: str | None = None,
157
+ extra: Mapping[str, object] | None = None,
158
+ sinfo: str | None = None,
159
+ ) -> logging.LogRecord:
160
+ record = super().makeRecord(name, level, fn, lno, msg, args, exc_info, func, extra, sinfo)
161
+ frame_info = _find_user_frame(self.extra_skip_packages)
162
+ if frame_info:
163
+ record.pathname = os.path.abspath(frame_info.filename)
164
+ record.filename = os.path.basename(frame_info.filename)
165
+ record.module = os.path.splitext(record.filename)[0]
166
+ record.funcName = frame_info.function
167
+ record.lineno = frame_info.lineno
168
+ return record
131
169
 
132
- # Overwrite standard location fields from the first user frame
133
- first = caller if isinstance(caller, str) else caller[0]
134
- self._overwrite_record_location(record, first)
135
170
 
136
- return True
171
+ def _install_object_logger(name: str) -> ObjectLogger:
172
+ """Create an ObjectLogger and register it in the logging system.
137
173
 
138
- @staticmethod
139
- def _overwrite_record_location(record: logging.LogRecord, frame_str: str) -> None:
140
- """Parse 'path/to/file.py:lineno in funcname' and overwrite record fields.
141
-
142
- Frame string format: "relative/path/to/file.py:42 in func_name"
143
- Uses rsplit to handle edge cases where function name could have spaces.
144
- """
145
- try:
146
- # Split off the function name part (rightmost " in ")
147
- path_part, func_part = frame_str.rsplit(" in ", 1)
148
- # Split off the line number (rightmost ":")
149
- filepath, lineno_str = path_part.rsplit(":", 1)
150
- record.pathname = os.path.abspath(filepath)
151
- record.filename = os.path.basename(filepath)
152
- record.module = os.path.splitext(record.filename)[0]
153
- record.funcName = func_part.strip()
154
- record.lineno = int(lineno_str)
155
- except (ValueError, AttributeError):
156
- pass # Keep original fields if parsing fails
174
+ Directly writes into logging.root.manager.loggerDict so that
175
+ logging.getLogger(name) returns this ObjectLogger instance.
176
+ Migrates handlers, level, and propagate from any pre-existing Logger
177
+ (but not from PlaceHolder sentinels, which are logging internals).
178
+
179
+ Thread-safe: acquires the logging module lock during the operation.
180
+
181
+ Known limitation: code that obtained a reference via getLogger(name)
182
+ *before* this function runs will still hold the old Logger instance.
183
+ """
184
+ with logging._lock: # type: ignore[attr-defined] # noqa: SLF001
185
+ existing = logging.root.manager.loggerDict.get(name)
186
+ logger = ObjectLogger(name)
187
+ logger.parent = logging.root
188
+ logger.propagate = True
189
+ if isinstance(existing, logging.Logger):
190
+ for handler in existing.handlers:
191
+ logger.addHandler(handler)
192
+ if existing.level != logging.NOTSET:
193
+ logger.setLevel(existing.level)
194
+ logger.propagate = existing.propagate
195
+ logging.root.manager.loggerDict[name] = logger
196
+ return logger
197
+
198
+
199
+ # Module-level instance — imported by executor.py and usable by callers
200
+ _sql_logger: ObjectLogger = _install_object_logger("sqlobjects.sql")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlobjects
3
- Version: 1.8.0
3
+ Version: 1.9.1
4
4
  Summary: Django-style async ORM library based on SQLAlchemy with chainable queries, Q objects, and relationship loading
5
5
  Author-email: XtraVisions <gitadmin@xtravisions.com>, Chen Hao <chenhao@xtravisions.com>
6
6
  Maintainer-email: XtraVisions <gitadmin@xtravisions.com>, Chen Hao <chenhao@xtravisions.com>
@@ -1,4 +1,4 @@
1
- sqlobjects/__init__.py,sha256=RLpjGDyRKjiR1ZFs2Gg831VHkJwHnuXP8eWZ1aCAshk,1101
1
+ sqlobjects/__init__.py,sha256=ALRG6rLlzRTecmSu1x8dA5Z7Y_XS3mOvYom8mlZuqJw,1095
2
2
  sqlobjects/_install_rules.py,sha256=ndw9815-60WY_ORGUefijUBnoUHPd0NBzUkCL8mQLKc,4053
3
3
  sqlobjects/cascade.py,sha256=QzNNJLf-jeh-p12Do7w2G8j-DYAVoqp1-cDATBhjRP8,39522
4
4
  sqlobjects/exceptions.py,sha256=IEp5XV6N3hc3SAU-C3F-5D6jEZU_p72mufvwVhaStHo,16592
@@ -8,7 +8,7 @@ sqlobjects/model.py,sha256=3cws701s1f788CTOoyTRHAKgZnH4rfn_c6tVx9wyTBs,19582
8
8
  sqlobjects/queryset.py,sha256=CjOWmUyuvGmcs2zTPRSrHwNpRwYCulYYz2ygw9IbIKc,50155
9
9
  sqlobjects/session.py,sha256=r11ZvIilL-ptFu1qZdmMEhYN9UxD1sq1qihRu2WUf7g,15542
10
10
  sqlobjects/signals.py,sha256=N4mhotmMy_Ub-XJCvdlfsQ767KVzPbbJdF0JlJ405Oc,17454
11
- sqlobjects/sql_logging.py,sha256=9ZZmqqfU8fJAgkXW0Abqd7JnNPmYTGH7enK0CFgMut4,5653
11
+ sqlobjects/sql_logging.py,sha256=LGnhKtLvoHcGVnclEG31rBavvD28iBREpvxsmmcxgu4,7370
12
12
  sqlobjects/validators.py,sha256=aqP7YgCnx-cjthqRo2RD-Iz2lvFsJsDgT73HwLwZGxE,10334
13
13
  sqlobjects/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
14
  sqlobjects/contrib/asgi.py,sha256=9-kOMAHA2xkKkgc2djec2sew6Og2dr3jGtQGd5oPrrM,1931
@@ -51,24 +51,24 @@ sqlobjects/objects/bulk.py,sha256=p81KZLQY90VKHjQCN7vuYPLh-TFcLE7h9b8MYVsHIi0,39
51
51
  sqlobjects/objects/core.py,sha256=uVRqFSLv-0cLdiGd9BS8c_an8jFD-S985oghWM_B46c,36666
52
52
  sqlobjects/objects/upsert.py,sha256=cqV3SEppI_mkWbhKFTslmMeVSuNREYzKy-HjUMexGTk,2516
53
53
  sqlobjects/queries/__init__.py,sha256=A9bXXsvht7HYjz8diDYLCEPX96Fr7T4eQh_iJfl4ToA,216
54
- sqlobjects/queries/builder.py,sha256=8RLs1te4wKTsYJViwBNX-ioDy2PV92kl6BXoNB7NmPw,26727
54
+ sqlobjects/queries/builder.py,sha256=hjNxYnddU1IWffDa5AtwaeG-d4_ZEcfg51IYLdVGOaE,26764
55
55
  sqlobjects/queries/dialect.py,sha256=_rMKbqKfdAvVoDwE17nF1lcL7nH7fyaNQHVhwD232i4,8003
56
- sqlobjects/queries/executor.py,sha256=YzOKXiyjqtuR7t8kfnSLYM2HiXeeITppZewbtXLdKBQ,18933
56
+ sqlobjects/queries/executor.py,sha256=6gc6Hwx72mUC4EeISBNCZ4YR0vdfTXsw8Yj1kmuIXP0,18920
57
57
  sqlobjects/utils/__init__.py,sha256=NsJ52Fl_PaB-2WlOIJVGNPmgOnqPmvYRmp7uaq5zfYs,192
58
58
  sqlobjects/utils/inspect.py,sha256=u-Q9VBoSGYCE_3Soy8jMuCAdM-P7IkSzTxqrJop_Ilk,2612
59
59
  sqlobjects/utils/naming.py,sha256=PmfNuEz-Twly2qREjKfMNulJt8TmOUJaxzFTuts3COg,1411
60
60
  sqlobjects/utils/pattern.py,sha256=FGCgobTpnmKjdAtQidHIg4Xa2CQLeP35F0VuX_1T1dk,14740
61
- sqlobjects-1.8.0.data/data/share/sqlobjects/rules/01-database-session-guide.md,sha256=m6WnTHyPAEI9z5-ehfQJQACaI7Hy0gcnzpgG1mW_Nhw,9220
62
- sqlobjects-1.8.0.data/data/share/sqlobjects/rules/02-model-definition-guide.md,sha256=i5gNJM-0bIJ2xSUu3mij8uB9kJC9EAc_439sCBqRph8,10977
63
- sqlobjects-1.8.0.data/data/share/sqlobjects/rules/03-query-operations-guide.md,sha256=U3ZAs-U020lnfxvzM7CKTrREegCf8zB7zBW7sJWxEAA,11867
64
- sqlobjects-1.8.0.data/data/share/sqlobjects/rules/04-crud-operations-guide.md,sha256=abUEvRPSiSjX8enclXO5Uryox9Rt0WFeRcFkvHXfPf8,10085
65
- sqlobjects-1.8.0.data/data/share/sqlobjects/rules/05-relationships-guide.md,sha256=PclRgzoCe_P1Pgy6dSvL6OZe2Ph_wBIkHtjktbufe7g,12368
66
- sqlobjects-1.8.0.data/data/share/sqlobjects/rules/06-validation-signals-guide.md,sha256=i9WLLhyCDoWGkIYqH2D_XLn98oMbF1pB3-L-b5gRbT4,14294
67
- sqlobjects-1.8.0.data/data/share/sqlobjects/rules/07-performance-guide.md,sha256=rjR0DjT39poEBVaUxAzJBEaMaBrZKLVer1zXaNNYfxg,12851
68
- sqlobjects-1.8.0.data/data/share/sqlobjects/rules/README.md,sha256=d2n5ZZT8LGvjL60u_HlzXn2uA8FwoCgqG3fuAhvMFvM,2280
69
- sqlobjects-1.8.0.dist-info/licenses/LICENSE,sha256=ScFR7nvWIhar0d32Y-LA4vqp9zNmy1HSJia7UX7jU6c,1068
70
- sqlobjects-1.8.0.dist-info/METADATA,sha256=5kP5QZh-83Ot--vd_lCwxZuF8nrZOAzZWrpiRFyH_r0,13740
71
- sqlobjects-1.8.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
72
- sqlobjects-1.8.0.dist-info/entry_points.txt,sha256=9Q3Ci55MawxkQhMlR_eLJui00_rYYIVO_Aq_Dm0u5MQ,76
73
- sqlobjects-1.8.0.dist-info/top_level.txt,sha256=brO5vDtCpn4L9dpX0EHpehbu4kcHaK_2Bbnwze35YKc,11
74
- sqlobjects-1.8.0.dist-info/RECORD,,
61
+ sqlobjects-1.9.1.data/data/share/sqlobjects/rules/01-database-session-guide.md,sha256=m6WnTHyPAEI9z5-ehfQJQACaI7Hy0gcnzpgG1mW_Nhw,9220
62
+ sqlobjects-1.9.1.data/data/share/sqlobjects/rules/02-model-definition-guide.md,sha256=i5gNJM-0bIJ2xSUu3mij8uB9kJC9EAc_439sCBqRph8,10977
63
+ sqlobjects-1.9.1.data/data/share/sqlobjects/rules/03-query-operations-guide.md,sha256=U3ZAs-U020lnfxvzM7CKTrREegCf8zB7zBW7sJWxEAA,11867
64
+ sqlobjects-1.9.1.data/data/share/sqlobjects/rules/04-crud-operations-guide.md,sha256=abUEvRPSiSjX8enclXO5Uryox9Rt0WFeRcFkvHXfPf8,10085
65
+ sqlobjects-1.9.1.data/data/share/sqlobjects/rules/05-relationships-guide.md,sha256=PclRgzoCe_P1Pgy6dSvL6OZe2Ph_wBIkHtjktbufe7g,12368
66
+ sqlobjects-1.9.1.data/data/share/sqlobjects/rules/06-validation-signals-guide.md,sha256=i9WLLhyCDoWGkIYqH2D_XLn98oMbF1pB3-L-b5gRbT4,14294
67
+ sqlobjects-1.9.1.data/data/share/sqlobjects/rules/07-performance-guide.md,sha256=rjR0DjT39poEBVaUxAzJBEaMaBrZKLVer1zXaNNYfxg,12851
68
+ sqlobjects-1.9.1.data/data/share/sqlobjects/rules/README.md,sha256=d2n5ZZT8LGvjL60u_HlzXn2uA8FwoCgqG3fuAhvMFvM,2280
69
+ sqlobjects-1.9.1.dist-info/licenses/LICENSE,sha256=ScFR7nvWIhar0d32Y-LA4vqp9zNmy1HSJia7UX7jU6c,1068
70
+ sqlobjects-1.9.1.dist-info/METADATA,sha256=hwm-cigR9lOxRkbN9L66dMxdKp9vc4gJGU1GNUGKHlw,13740
71
+ sqlobjects-1.9.1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
72
+ sqlobjects-1.9.1.dist-info/entry_points.txt,sha256=9Q3Ci55MawxkQhMlR_eLJui00_rYYIVO_Aq_Dm0u5MQ,76
73
+ sqlobjects-1.9.1.dist-info/top_level.txt,sha256=brO5vDtCpn4L9dpX0EHpehbu4kcHaK_2Bbnwze35YKc,11
74
+ sqlobjects-1.9.1.dist-info/RECORD,,