sqlsaber 0.28.0__py3-none-any.whl → 0.29.0__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.
Potentially problematic release.
This version of sqlsaber might be problematic. Click here for more details.
- sqlsaber/cli/commands.py +2 -0
- sqlsaber/cli/theme.py +146 -0
- sqlsaber/theme/manager.py +87 -77
- {sqlsaber-0.28.0.dist-info → sqlsaber-0.29.0.dist-info}/METADATA +1 -1
- {sqlsaber-0.28.0.dist-info → sqlsaber-0.29.0.dist-info}/RECORD +8 -7
- {sqlsaber-0.28.0.dist-info → sqlsaber-0.29.0.dist-info}/WHEEL +0 -0
- {sqlsaber-0.28.0.dist-info → sqlsaber-0.29.0.dist-info}/entry_points.txt +0 -0
- {sqlsaber-0.28.0.dist-info → sqlsaber-0.29.0.dist-info}/licenses/LICENSE +0 -0
sqlsaber/cli/commands.py
CHANGED
|
@@ -11,6 +11,7 @@ from sqlsaber.cli.database import create_db_app
|
|
|
11
11
|
from sqlsaber.cli.memory import create_memory_app
|
|
12
12
|
from sqlsaber.cli.models import create_models_app
|
|
13
13
|
from sqlsaber.cli.onboarding import needs_onboarding, run_onboarding
|
|
14
|
+
from sqlsaber.cli.theme import create_theme_app
|
|
14
15
|
from sqlsaber.cli.threads import create_threads_app
|
|
15
16
|
|
|
16
17
|
# Lazy imports - only import what's needed for CLI parsing
|
|
@@ -35,6 +36,7 @@ app.command(create_auth_app(), name="auth")
|
|
|
35
36
|
app.command(create_db_app(), name="db")
|
|
36
37
|
app.command(create_memory_app(), name="memory")
|
|
37
38
|
app.command(create_models_app(), name="models")
|
|
39
|
+
app.command(create_theme_app(), name="theme")
|
|
38
40
|
app.command(create_threads_app(), name="threads")
|
|
39
41
|
|
|
40
42
|
console = create_console()
|
sqlsaber/cli/theme.py
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"""Theme management CLI commands."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import cyclopts
|
|
10
|
+
import questionary
|
|
11
|
+
from platformdirs import user_config_dir
|
|
12
|
+
from pygments.styles import get_all_styles
|
|
13
|
+
|
|
14
|
+
from sqlsaber.theme.manager import DEFAULT_THEME_NAME, create_console
|
|
15
|
+
|
|
16
|
+
console = create_console()
|
|
17
|
+
|
|
18
|
+
# Create the theme management CLI app
|
|
19
|
+
theme_app = cyclopts.App(
|
|
20
|
+
name="theme",
|
|
21
|
+
help="Manage theme settings",
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ThemeManager:
|
|
26
|
+
"""Manages theme configuration persistence."""
|
|
27
|
+
|
|
28
|
+
def __init__(self):
|
|
29
|
+
self.config_dir = Path(user_config_dir("sqlsaber"))
|
|
30
|
+
self.config_file = self.config_dir / "theme.json"
|
|
31
|
+
|
|
32
|
+
def _ensure_config_dir(self) -> None:
|
|
33
|
+
"""Ensure config directory exists."""
|
|
34
|
+
self.config_dir.mkdir(parents=True, exist_ok=True)
|
|
35
|
+
|
|
36
|
+
def _load_config(self) -> dict:
|
|
37
|
+
"""Load theme configuration from file."""
|
|
38
|
+
if not self.config_file.exists():
|
|
39
|
+
return {}
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
with open(self.config_file, "r") as f:
|
|
43
|
+
return json.load(f)
|
|
44
|
+
except Exception:
|
|
45
|
+
return {}
|
|
46
|
+
|
|
47
|
+
def _save_config(self, config: dict) -> None:
|
|
48
|
+
"""Save theme configuration to file."""
|
|
49
|
+
self._ensure_config_dir()
|
|
50
|
+
|
|
51
|
+
with open(self.config_file, "w") as f:
|
|
52
|
+
json.dump(config, f, indent=2)
|
|
53
|
+
|
|
54
|
+
def get_current_theme(self) -> str:
|
|
55
|
+
"""Get the currently configured theme."""
|
|
56
|
+
config = self._load_config()
|
|
57
|
+
env_theme = os.getenv("SQLSABER_THEME")
|
|
58
|
+
if env_theme:
|
|
59
|
+
return env_theme
|
|
60
|
+
return config.get("theme", {}).get("pygments_style") or DEFAULT_THEME_NAME
|
|
61
|
+
|
|
62
|
+
def set_theme(self, theme_name: str) -> bool:
|
|
63
|
+
"""Set the current theme."""
|
|
64
|
+
try:
|
|
65
|
+
config = self._load_config()
|
|
66
|
+
if "theme" not in config:
|
|
67
|
+
config["theme"] = {}
|
|
68
|
+
config["theme"]["name"] = theme_name
|
|
69
|
+
config["theme"]["pygments_style"] = theme_name
|
|
70
|
+
self._save_config(config)
|
|
71
|
+
return True
|
|
72
|
+
except Exception as e:
|
|
73
|
+
console.print(f"[error]Error setting theme: {e}[/error]")
|
|
74
|
+
return False
|
|
75
|
+
|
|
76
|
+
def reset_theme(self) -> bool:
|
|
77
|
+
"""Reset to default theme."""
|
|
78
|
+
try:
|
|
79
|
+
if self.config_file.exists():
|
|
80
|
+
self.config_file.unlink()
|
|
81
|
+
return True
|
|
82
|
+
except Exception as e:
|
|
83
|
+
console.print(f"[error]Error resetting theme: {e}[/error]")
|
|
84
|
+
return False
|
|
85
|
+
|
|
86
|
+
def get_available_themes(self) -> list[str]:
|
|
87
|
+
"""Get list of available Pygments themes."""
|
|
88
|
+
return sorted(get_all_styles())
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
theme_manager = ThemeManager()
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@theme_app.command
|
|
95
|
+
def set():
|
|
96
|
+
"""Set the theme to use for syntax highlighting."""
|
|
97
|
+
|
|
98
|
+
async def interactive_set():
|
|
99
|
+
themes = theme_manager.get_available_themes()
|
|
100
|
+
current_theme = theme_manager.get_current_theme()
|
|
101
|
+
|
|
102
|
+
# Create choices with current theme highlighted
|
|
103
|
+
choices = [
|
|
104
|
+
questionary.Choice(
|
|
105
|
+
title=f"{theme} (current)" if theme == current_theme else theme,
|
|
106
|
+
value=theme,
|
|
107
|
+
)
|
|
108
|
+
for theme in themes
|
|
109
|
+
]
|
|
110
|
+
|
|
111
|
+
selected_theme = await questionary.select(
|
|
112
|
+
"Select a theme:",
|
|
113
|
+
choices=choices,
|
|
114
|
+
default=current_theme,
|
|
115
|
+
use_search_filter=True,
|
|
116
|
+
use_jk_keys=False,
|
|
117
|
+
).ask_async()
|
|
118
|
+
|
|
119
|
+
if selected_theme:
|
|
120
|
+
if theme_manager.set_theme(selected_theme):
|
|
121
|
+
console.print(f"[success]✓ Theme set to: {selected_theme}[/success]")
|
|
122
|
+
else:
|
|
123
|
+
console.print("[error]✗ Failed to set theme[/error]")
|
|
124
|
+
sys.exit(1)
|
|
125
|
+
else:
|
|
126
|
+
console.print("[warning]Operation cancelled[/warning]")
|
|
127
|
+
|
|
128
|
+
asyncio.run(interactive_set())
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@theme_app.command
|
|
132
|
+
def reset():
|
|
133
|
+
"""Reset to the default theme."""
|
|
134
|
+
|
|
135
|
+
if theme_manager.reset_theme():
|
|
136
|
+
console.print(
|
|
137
|
+
f"[success]✓ Theme reset to default: {DEFAULT_THEME_NAME}[/success]"
|
|
138
|
+
)
|
|
139
|
+
else:
|
|
140
|
+
console.print("[error]✗ Failed to reset theme[/error]")
|
|
141
|
+
sys.exit(1)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def create_theme_app() -> cyclopts.App:
|
|
145
|
+
"""Return the theme management CLI app."""
|
|
146
|
+
return theme_app
|
sqlsaber/theme/manager.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Theme management for unified theming across Rich and prompt_toolkit."""
|
|
2
2
|
|
|
3
|
+
import json
|
|
3
4
|
import os
|
|
4
|
-
import tomllib
|
|
5
5
|
from dataclasses import dataclass
|
|
6
6
|
from functools import lru_cache
|
|
7
7
|
from typing import Dict
|
|
@@ -10,6 +10,8 @@ from platformdirs import user_config_dir
|
|
|
10
10
|
from prompt_toolkit.styles import Style as PTStyle
|
|
11
11
|
from prompt_toolkit.styles.pygments import style_from_pygments_cls
|
|
12
12
|
from pygments.styles import get_style_by_name
|
|
13
|
+
from pygments.token import Token
|
|
14
|
+
from pygments.util import ClassNotFound
|
|
13
15
|
from rich.console import Console
|
|
14
16
|
from rich.theme import Theme
|
|
15
17
|
|
|
@@ -43,89 +45,97 @@ DEFAULT_ROLE_PALETTE = {
|
|
|
43
45
|
"title": "bold $success",
|
|
44
46
|
}
|
|
45
47
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
"
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
"error": "#dc322f", # (using solarized red as fallback)
|
|
85
|
-
"info": "#2aa198", # (using solarized cyan as fallback)
|
|
86
|
-
"muted": "dim",
|
|
87
|
-
},
|
|
88
|
-
# Material (approximation based on material design colors)
|
|
89
|
-
"material": {
|
|
90
|
-
"primary": "#89ddff", # cyan
|
|
91
|
-
"accent": "#f07178", # pink/red
|
|
92
|
-
"success": "#c3e88d", # green
|
|
93
|
-
"warning": "#ffcb6b", # yellow
|
|
94
|
-
"error": "#ff5370", # red
|
|
95
|
-
"info": "#82aaff", # blue
|
|
96
|
-
"muted": "dim",
|
|
97
|
-
},
|
|
98
|
-
# One Dark - exact colors from pygments one-dark theme
|
|
99
|
-
"one-dark": {
|
|
100
|
-
"primary": "#c678dd", # Keyword (purple)
|
|
101
|
-
"accent": "#e06c75", # Name (red)
|
|
102
|
-
"success": "#98c379", # String (green)
|
|
103
|
-
"warning": "#e5c07b", # Keyword.Type (yellow)
|
|
104
|
-
"error": "#e06c75", # Name (red, used for errors)
|
|
105
|
-
"info": "#61afef", # Name.Function (blue)
|
|
106
|
-
"muted": "dim",
|
|
107
|
-
},
|
|
108
|
-
# Lightbulb - exact colors from pygments lightbulb theme (minimal dark)
|
|
109
|
-
"lightbulb": {
|
|
110
|
-
"primary": "#73d0ff", # Keyword.Type/Name.Class (blue_1)
|
|
111
|
-
"accent": "#dfbfff", # Number (magenta_1)
|
|
112
|
-
"success": "#d5ff80", # String (green_1)
|
|
113
|
-
"warning": "#ffd173", # Name.Function (yellow_1)
|
|
114
|
-
"error": "#f88f7f", # Error (red_1)
|
|
115
|
-
"info": "#95e6cb", # Name.Entity (cyan_1)
|
|
116
|
-
"muted": "dim",
|
|
117
|
-
},
|
|
48
|
+
ROLE_TOKEN_PREFERENCES: dict[str, tuple] = {
|
|
49
|
+
"primary": (
|
|
50
|
+
Token.Keyword,
|
|
51
|
+
Token.Keyword.Namespace,
|
|
52
|
+
Token.Name.Tag,
|
|
53
|
+
),
|
|
54
|
+
"accent": (
|
|
55
|
+
Token.Name.Tag,
|
|
56
|
+
Token.Keyword.Type,
|
|
57
|
+
Token.Literal.Number,
|
|
58
|
+
Token.Operator.Word,
|
|
59
|
+
),
|
|
60
|
+
"success": (
|
|
61
|
+
Token.Literal.String,
|
|
62
|
+
Token.Generic.Inserted,
|
|
63
|
+
Token.Name.Attribute,
|
|
64
|
+
),
|
|
65
|
+
"warning": (
|
|
66
|
+
Token.Literal.String.Escape,
|
|
67
|
+
Token.Name.Constant,
|
|
68
|
+
Token.Generic.Emph,
|
|
69
|
+
),
|
|
70
|
+
"error": (
|
|
71
|
+
Token.Error,
|
|
72
|
+
Token.Generic.Error,
|
|
73
|
+
Token.Generic.Deleted,
|
|
74
|
+
Token.Name.Exception,
|
|
75
|
+
),
|
|
76
|
+
"info": (
|
|
77
|
+
Token.Name.Function,
|
|
78
|
+
Token.Name.Builtin,
|
|
79
|
+
Token.Keyword.Type,
|
|
80
|
+
),
|
|
81
|
+
"muted": (
|
|
82
|
+
Token.Comment,
|
|
83
|
+
Token.Generic.Subheading,
|
|
84
|
+
Token.Text,
|
|
85
|
+
),
|
|
118
86
|
}
|
|
119
87
|
|
|
120
88
|
|
|
89
|
+
def _normalize_hex(color: str | None) -> str | None:
|
|
90
|
+
if not color:
|
|
91
|
+
return None
|
|
92
|
+
color = color.strip()
|
|
93
|
+
if not color:
|
|
94
|
+
return None
|
|
95
|
+
if color.startswith("#"):
|
|
96
|
+
color = color[1:]
|
|
97
|
+
if len(color) == 3:
|
|
98
|
+
color = "".join(ch * 2 for ch in color)
|
|
99
|
+
if len(color) != 6:
|
|
100
|
+
return None
|
|
101
|
+
return f"#{color.lower()}"
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _build_role_palette_from_style(style_name: str) -> dict[str, str]:
|
|
105
|
+
try:
|
|
106
|
+
style_cls = get_style_by_name(style_name)
|
|
107
|
+
except ClassNotFound:
|
|
108
|
+
return {}
|
|
109
|
+
|
|
110
|
+
palette: dict[str, str] = {}
|
|
111
|
+
try:
|
|
112
|
+
base_color = _normalize_hex(style_cls.style_for_token(Token.Text).get("color"))
|
|
113
|
+
except KeyError:
|
|
114
|
+
base_color = None
|
|
115
|
+
for role, tokens in ROLE_TOKEN_PREFERENCES.items():
|
|
116
|
+
for token in tokens:
|
|
117
|
+
try:
|
|
118
|
+
style_def = style_cls.style_for_token(token)
|
|
119
|
+
except KeyError:
|
|
120
|
+
continue
|
|
121
|
+
color = _normalize_hex(style_def.get("color"))
|
|
122
|
+
if not color or color == base_color:
|
|
123
|
+
continue
|
|
124
|
+
if role == "accent" and color == palette.get("primary"):
|
|
125
|
+
continue
|
|
126
|
+
palette[role] = color
|
|
127
|
+
break
|
|
128
|
+
return palette
|
|
129
|
+
|
|
130
|
+
|
|
121
131
|
def _load_user_theme_config() -> dict:
|
|
122
132
|
"""Load theme configuration from user config directory."""
|
|
123
133
|
cfg_dir = user_config_dir("sqlsaber")
|
|
124
|
-
path = os.path.join(cfg_dir, "theme.
|
|
134
|
+
path = os.path.join(cfg_dir, "theme.json")
|
|
125
135
|
if not os.path.exists(path):
|
|
126
136
|
return {}
|
|
127
|
-
with open(path, "
|
|
128
|
-
return
|
|
137
|
+
with open(path, "r") as f:
|
|
138
|
+
return json.load(f)
|
|
129
139
|
|
|
130
140
|
|
|
131
141
|
def _resolve_refs(palette: dict[str, str]) -> dict[str, str]:
|
|
@@ -204,7 +214,7 @@ def get_theme_manager() -> ThemeManager:
|
|
|
204
214
|
pygments_style = user_cfg.get("theme", {}).get("pygments_style") or name
|
|
205
215
|
|
|
206
216
|
roles = dict(DEFAULT_ROLE_PALETTE)
|
|
207
|
-
roles.update(
|
|
217
|
+
roles.update(_build_role_palette_from_style(pygments_style))
|
|
208
218
|
roles.update(user_cfg.get("roles", {}))
|
|
209
219
|
|
|
210
220
|
cfg = ThemeConfig(name=name, pygments_style=pygments_style, roles=roles)
|
|
@@ -11,7 +11,7 @@ sqlsaber/application/model_selection.py,sha256=xZI-nvUgYZikaTK38SCmEWvWSfRsDpFu2
|
|
|
11
11
|
sqlsaber/application/prompts.py,sha256=4rMGcWpYJbNWPMzqVWseUMx0nwvXOkWS6GaTAJ5mhfc,3473
|
|
12
12
|
sqlsaber/cli/__init__.py,sha256=qVSLVJLLJYzoC6aj6y9MFrzZvAwc4_OgxU9DlkQnZ4M,86
|
|
13
13
|
sqlsaber/cli/auth.py,sha256=ysDBXEFR8Jz7wYbIP6X7yWA2ivd8SDnUp_jUg_qYNWk,6088
|
|
14
|
-
sqlsaber/cli/commands.py,sha256
|
|
14
|
+
sqlsaber/cli/commands.py,sha256=LiHLMAZF9fNgQXShtF67dzDvp-w4LlSbmeZIa47ntC0,8634
|
|
15
15
|
sqlsaber/cli/completers.py,sha256=g-hLDq5fiBx7gg8Bte1Lq8GU-ZxCYVs4dcPsmHPIcK4,6574
|
|
16
16
|
sqlsaber/cli/database.py,sha256=hh8PdWnhaD0fO2jwvSSQyxsjwk-JyvmcY7f5tuHfnAQ,10663
|
|
17
17
|
sqlsaber/cli/display.py,sha256=WB5JCumhXadziDEX1EZHG3vN1Chol5FNAaTXHieqFK0,17892
|
|
@@ -20,6 +20,7 @@ sqlsaber/cli/memory.py,sha256=gKP-JJ0w1ya1YTM_Lk7Gw-7wL9ptyj6cZtg-uoW8K7A,7818
|
|
|
20
20
|
sqlsaber/cli/models.py,sha256=NozZbnisSjbPKo7PW7CltJMIkGcPqTDpDQEY-C_eLhk,8504
|
|
21
21
|
sqlsaber/cli/onboarding.py,sha256=l6FFWn8J1OVQUxr-xIAzKaFhAz8rFh6IEWwIyPWqR6U,11438
|
|
22
22
|
sqlsaber/cli/streaming.py,sha256=1XoZGPPMoTmBQVgp_Bqk483MR93j9oXxSV6Tx_-TpOg,6923
|
|
23
|
+
sqlsaber/cli/theme.py,sha256=hP0kmsMLCtqaT7b5wB1dk1hW1hV94oP4BHdz8S6887A,4243
|
|
23
24
|
sqlsaber/cli/threads.py,sha256=5EV4ckRzKqhWeTKpTfQSNCBuqs3onsJURKT09g4E4XM,13634
|
|
24
25
|
sqlsaber/config/__init__.py,sha256=olwC45k8Nc61yK0WmPUk7XHdbsZH9HuUAbwnmKe3IgA,100
|
|
25
26
|
sqlsaber/config/api_keys.py,sha256=bjogRmIuxNNGusyKXKi0ZpJWeS5Fyn53zrAD8hsoYx4,3671
|
|
@@ -44,7 +45,7 @@ sqlsaber/memory/__init__.py,sha256=GiWkU6f6YYVV0EvvXDmFWe_CxarmDCql05t70MkTEWs,6
|
|
|
44
45
|
sqlsaber/memory/manager.py,sha256=p3fybMVfH-E4ApT1ZRZUnQIWSk9dkfUPCyfkmA0HALs,2739
|
|
45
46
|
sqlsaber/memory/storage.py,sha256=ne8szLlGj5NELheqLnI7zu21V8YS4rtpYGGC7tOmi-s,5745
|
|
46
47
|
sqlsaber/theme/__init__.py,sha256=qCICX1Cg4B6yCbZ1UrerxglWxcqldRFVSRrSs73na_8,188
|
|
47
|
-
sqlsaber/theme/manager.py,sha256=
|
|
48
|
+
sqlsaber/theme/manager.py,sha256=7QXoqPvl-aKfvqTRduWYwnHsySu66Gg8a3-QQkVM5Ss,6551
|
|
48
49
|
sqlsaber/threads/__init__.py,sha256=Hh3dIG1tuC8fXprREUpslCIgPYz8_6o7aRLx4yNeO48,139
|
|
49
50
|
sqlsaber/threads/storage.py,sha256=rsUdxT4CR52D7xtGir9UlsFnBMk11jZeflzDrk2q4ME,11183
|
|
50
51
|
sqlsaber/tools/__init__.py,sha256=x3YdmX_7P0Qq_HtZHAgfIVKTLxYqKk6oc4tGsujQWsc,586
|
|
@@ -54,8 +55,8 @@ sqlsaber/tools/instructions.py,sha256=X-x8maVkkyi16b6Tl0hcAFgjiYceZaSwyWTfmrvx8U
|
|
|
54
55
|
sqlsaber/tools/registry.py,sha256=HWOQMsNIdL4XZS6TeNUyrL-5KoSDH6PHsWd3X66o-18,3211
|
|
55
56
|
sqlsaber/tools/sql_guard.py,sha256=dTDwcZP-N4xPGzcr7MQtKUxKrlDzlc1irr9aH5a4wvk,6182
|
|
56
57
|
sqlsaber/tools/sql_tools.py,sha256=ujmAcfLkNaBrb5LWEgWcINQEQSX0LRPX3VK5Dag1Sj4,9178
|
|
57
|
-
sqlsaber-0.
|
|
58
|
-
sqlsaber-0.
|
|
59
|
-
sqlsaber-0.
|
|
60
|
-
sqlsaber-0.
|
|
61
|
-
sqlsaber-0.
|
|
58
|
+
sqlsaber-0.29.0.dist-info/METADATA,sha256=5Ocm_GD3MIdPLji_VpknTjiQ0ndDd2waD14dr6_IOjg,7174
|
|
59
|
+
sqlsaber-0.29.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
60
|
+
sqlsaber-0.29.0.dist-info/entry_points.txt,sha256=qEbOB7OffXPFgyJc7qEIJlMEX5RN9xdzLmWZa91zCQQ,162
|
|
61
|
+
sqlsaber-0.29.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
62
|
+
sqlsaber-0.29.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|