tinycontracts 0.1.1__tar.gz → 0.1.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.
- {tinycontracts-0.1.1 → tinycontracts-0.1.2}/PKG-INFO +24 -32
- tinycontracts-0.1.2/README.md +61 -0
- {tinycontracts-0.1.1 → tinycontracts-0.1.2}/pyproject.toml +2 -2
- tinycontracts-0.1.2/tinycontracts/cli.py +153 -0
- {tinycontracts-0.1.1 → tinycontracts-0.1.2}/uv.lock +1 -1
- tinycontracts-0.1.1/README.md +0 -69
- tinycontracts-0.1.1/tinycontracts/cli.py +0 -134
- {tinycontracts-0.1.1 → tinycontracts-0.1.2}/.gitignore +0 -0
- {tinycontracts-0.1.1 → tinycontracts-0.1.2}/.python-version +0 -0
- {tinycontracts-0.1.1 → tinycontracts-0.1.2}/tinycontracts/__init__.py +0 -0
- {tinycontracts-0.1.1 → tinycontracts-0.1.2}/tinycontracts/loader.py +0 -0
- {tinycontracts-0.1.1 → tinycontracts-0.1.2}/tinycontracts/routes.py +0 -0
- {tinycontracts-0.1.1 → tinycontracts-0.1.2}/tinycontracts/schema.py +0 -0
- {tinycontracts-0.1.1 → tinycontracts-0.1.2}/tinycontracts/server.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tinycontracts
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: serve a folder of json/csv/parquet files as a rest api
|
|
5
5
|
Author-email: Aditya Kumar <adityakuma0308@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -24,7 +24,7 @@ Description-Content-Type: text/markdown
|
|
|
24
24
|
|
|
25
25
|
# tinycontracts
|
|
26
26
|
|
|
27
|
-
Turn JSON, CSV,
|
|
27
|
+
Turn any folder of JSON, CSV, or Parquet files into a REST API instantly.
|
|
28
28
|
|
|
29
29
|
## Install
|
|
30
30
|
|
|
@@ -38,56 +38,48 @@ pip install tinycontracts
|
|
|
38
38
|
tc ./data
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
That's it. Your files are now API endpoints.
|
|
42
|
+
|
|
43
|
+
## Options
|
|
42
44
|
|
|
43
45
|
```bash
|
|
44
|
-
tc ./data
|
|
46
|
+
tc ./data -p 8000 # custom port (default: 4242)
|
|
47
|
+
tc ./data -H 0.0.0.0 # expose to network
|
|
48
|
+
tc --version # show version
|
|
45
49
|
```
|
|
46
50
|
|
|
47
51
|
## Example
|
|
48
52
|
|
|
49
|
-
Given this structure:
|
|
50
53
|
```
|
|
51
54
|
data/
|
|
52
|
-
users.json
|
|
53
|
-
orders.csv
|
|
54
|
-
config.json
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
You get:
|
|
58
|
-
```
|
|
59
|
-
GET /users # list all users
|
|
60
|
-
GET /users/1 # get user by id
|
|
61
|
-
GET /orders # list all orders
|
|
62
|
-
GET /config # get config
|
|
63
|
-
|
|
64
|
-
GET /_help # api documentation
|
|
65
|
-
GET /_schema # all schemas
|
|
66
|
-
GET /docs # swagger ui
|
|
55
|
+
users.json -> GET /users
|
|
56
|
+
orders.csv -> GET /orders
|
|
57
|
+
config.json -> GET /config
|
|
67
58
|
```
|
|
68
59
|
|
|
69
|
-
##
|
|
60
|
+
## Querying
|
|
70
61
|
|
|
71
62
|
```bash
|
|
72
|
-
# filter
|
|
63
|
+
# filter
|
|
73
64
|
curl "localhost:4242/users?active=true"
|
|
74
65
|
|
|
75
66
|
# pagination
|
|
76
67
|
curl "localhost:4242/orders?_limit=10&_offset=20"
|
|
77
68
|
|
|
78
|
-
#
|
|
69
|
+
# sort
|
|
79
70
|
curl "localhost:4242/orders?_sort=-created_at"
|
|
80
71
|
|
|
81
72
|
# combine
|
|
82
|
-
curl "localhost:4242/orders?status=pending&_limit=5
|
|
73
|
+
curl "localhost:4242/orders?status=pending&_limit=5"
|
|
83
74
|
```
|
|
84
75
|
|
|
85
|
-
##
|
|
76
|
+
## Endpoints
|
|
86
77
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
78
|
+
| Endpoint | Description |
|
|
79
|
+
|----------|-------------|
|
|
80
|
+
| `/{resource}` | List all rows |
|
|
81
|
+
| `/{resource}/{id}` | Get by ID |
|
|
82
|
+
| `/{resource}/_schema` | JSON schema |
|
|
83
|
+
| `/_help` | API documentation |
|
|
84
|
+
| `/_schema` | All schemas |
|
|
85
|
+
| `/docs` | Swagger UI |
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# tinycontracts
|
|
2
|
+
|
|
3
|
+
Turn any folder of JSON, CSV, or Parquet files into a REST API instantly.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install tinycontracts
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
tc ./data
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
That's it. Your files are now API endpoints.
|
|
18
|
+
|
|
19
|
+
## Options
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
tc ./data -p 8000 # custom port (default: 4242)
|
|
23
|
+
tc ./data -H 0.0.0.0 # expose to network
|
|
24
|
+
tc --version # show version
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Example
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
data/
|
|
31
|
+
users.json -> GET /users
|
|
32
|
+
orders.csv -> GET /orders
|
|
33
|
+
config.json -> GET /config
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Querying
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# filter
|
|
40
|
+
curl "localhost:4242/users?active=true"
|
|
41
|
+
|
|
42
|
+
# pagination
|
|
43
|
+
curl "localhost:4242/orders?_limit=10&_offset=20"
|
|
44
|
+
|
|
45
|
+
# sort
|
|
46
|
+
curl "localhost:4242/orders?_sort=-created_at"
|
|
47
|
+
|
|
48
|
+
# combine
|
|
49
|
+
curl "localhost:4242/orders?status=pending&_limit=5"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Endpoints
|
|
53
|
+
|
|
54
|
+
| Endpoint | Description |
|
|
55
|
+
|----------|-------------|
|
|
56
|
+
| `/{resource}` | List all rows |
|
|
57
|
+
| `/{resource}/{id}` | Get by ID |
|
|
58
|
+
| `/{resource}/_schema` | JSON schema |
|
|
59
|
+
| `/_help` | API documentation |
|
|
60
|
+
| `/_schema` | All schemas |
|
|
61
|
+
| `/docs` | Swagger UI |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "tinycontracts"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.2"
|
|
4
4
|
description = "serve a folder of json/csv/parquet files as a rest api"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -29,7 +29,7 @@ dependencies = [
|
|
|
29
29
|
]
|
|
30
30
|
|
|
31
31
|
[project.scripts]
|
|
32
|
-
tc = "tinycontracts.cli:
|
|
32
|
+
tc = "tinycontracts.cli:main"
|
|
33
33
|
|
|
34
34
|
[build-system]
|
|
35
35
|
requires = ["hatchling"]
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# cli.py - fast startup with lazy imports
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def main():
|
|
6
|
+
# fast path for --help and --version (no heavy imports)
|
|
7
|
+
args = sys.argv[1:]
|
|
8
|
+
|
|
9
|
+
if not args or args[0] in ("--help", "-h"):
|
|
10
|
+
print("""tc - serve data files as a rest api
|
|
11
|
+
|
|
12
|
+
usage: tc <folder> [options]
|
|
13
|
+
|
|
14
|
+
arguments:
|
|
15
|
+
folder folder containing json/csv/parquet files
|
|
16
|
+
|
|
17
|
+
options:
|
|
18
|
+
-p, --port PORT port to serve on (default: 4242)
|
|
19
|
+
-H, --host HOST host to bind to (default: 127.0.0.1)
|
|
20
|
+
-v, --version show version
|
|
21
|
+
-h, --help show this help""")
|
|
22
|
+
return
|
|
23
|
+
|
|
24
|
+
if args[0] in ("--version", "-v"):
|
|
25
|
+
print("tinycontracts v0.1.2")
|
|
26
|
+
return
|
|
27
|
+
|
|
28
|
+
# parse args manually for fast path
|
|
29
|
+
path = None
|
|
30
|
+
port = 4242
|
|
31
|
+
host = "127.0.0.1"
|
|
32
|
+
|
|
33
|
+
i = 0
|
|
34
|
+
while i < len(args):
|
|
35
|
+
arg = args[i]
|
|
36
|
+
if arg in ("-p", "--port"):
|
|
37
|
+
port = int(args[i + 1])
|
|
38
|
+
i += 2
|
|
39
|
+
elif arg in ("-H", "--host"):
|
|
40
|
+
host = args[i + 1]
|
|
41
|
+
i += 2
|
|
42
|
+
elif arg.startswith("-"):
|
|
43
|
+
print(f"error: unknown option {arg}")
|
|
44
|
+
sys.exit(1)
|
|
45
|
+
else:
|
|
46
|
+
path = arg
|
|
47
|
+
i += 1
|
|
48
|
+
|
|
49
|
+
if not path:
|
|
50
|
+
print("error: missing folder path\nusage: tc <folder>")
|
|
51
|
+
sys.exit(1)
|
|
52
|
+
|
|
53
|
+
# now import heavy deps
|
|
54
|
+
from pathlib import Path
|
|
55
|
+
from rich.console import Console
|
|
56
|
+
|
|
57
|
+
console = Console()
|
|
58
|
+
folder = Path(path)
|
|
59
|
+
|
|
60
|
+
if not folder.exists():
|
|
61
|
+
console.print(f"\n [red]error:[/red] path not found: [bold]{folder}[/bold]")
|
|
62
|
+
console.print(" [dim]make sure the path exists and try again[/dim]\n")
|
|
63
|
+
sys.exit(1)
|
|
64
|
+
|
|
65
|
+
if not folder.is_dir():
|
|
66
|
+
console.print(f"\n [red]error:[/red] not a folder: [bold]{folder}[/bold]")
|
|
67
|
+
console.print(" [dim]tc needs a folder path, not a file[/dim]\n")
|
|
68
|
+
sys.exit(1)
|
|
69
|
+
|
|
70
|
+
data_files = (
|
|
71
|
+
list(folder.glob("*.json"))
|
|
72
|
+
+ list(folder.glob("*.csv"))
|
|
73
|
+
+ list(folder.glob("*.parquet"))
|
|
74
|
+
)
|
|
75
|
+
if not data_files:
|
|
76
|
+
console.print(
|
|
77
|
+
f"\n [yellow]warning:[/yellow] no data files in [bold]{folder}[/bold]"
|
|
78
|
+
)
|
|
79
|
+
console.print(" [dim]add .json, .csv, or .parquet files[/dim]\n")
|
|
80
|
+
|
|
81
|
+
# lazy import heavy deps with spinner
|
|
82
|
+
with console.status("[cyan]loading...[/cyan]", spinner="dots"):
|
|
83
|
+
from tinycontracts.server import create_app
|
|
84
|
+
import uvicorn
|
|
85
|
+
|
|
86
|
+
try:
|
|
87
|
+
fastapi_app = create_app(folder)
|
|
88
|
+
resources = (
|
|
89
|
+
list(fastapi_app.state.resources.keys())
|
|
90
|
+
if hasattr(fastapi_app.state, "resources")
|
|
91
|
+
else []
|
|
92
|
+
)
|
|
93
|
+
except PermissionError:
|
|
94
|
+
console.print(f"\n [red]error:[/red] permission denied: [bold]{folder}[/bold]")
|
|
95
|
+
console.print(" [dim]check file permissions and try again[/dim]\n")
|
|
96
|
+
sys.exit(1)
|
|
97
|
+
except Exception as e:
|
|
98
|
+
console.print(f"\n [red]error:[/red] failed to load data: {e}\n")
|
|
99
|
+
sys.exit(1)
|
|
100
|
+
|
|
101
|
+
# print banner
|
|
102
|
+
from rich.panel import Panel
|
|
103
|
+
from rich.table import Table
|
|
104
|
+
|
|
105
|
+
console.print()
|
|
106
|
+
console.print(
|
|
107
|
+
Panel.fit(
|
|
108
|
+
"[bold cyan]tinycontracts[/bold cyan] [dim]v0.1.2[/dim]",
|
|
109
|
+
border_style="cyan",
|
|
110
|
+
)
|
|
111
|
+
)
|
|
112
|
+
console.print()
|
|
113
|
+
|
|
114
|
+
console.print(f" [dim]folder:[/dim] {folder.resolve()}")
|
|
115
|
+
console.print(f" [dim]server:[/dim] [green]http://{host}:{port}[/green]")
|
|
116
|
+
console.print()
|
|
117
|
+
|
|
118
|
+
if resources:
|
|
119
|
+
table = Table(show_header=True, header_style="bold", box=None, padding=(0, 2))
|
|
120
|
+
table.add_column("endpoint", style="green")
|
|
121
|
+
table.add_column("description", style="dim")
|
|
122
|
+
|
|
123
|
+
for name in sorted(resources):
|
|
124
|
+
table.add_row(f"/{name}", f"query {name}")
|
|
125
|
+
|
|
126
|
+
table.add_row("", "")
|
|
127
|
+
table.add_row("/docs", "swagger ui")
|
|
128
|
+
table.add_row("/_help", "api help")
|
|
129
|
+
|
|
130
|
+
console.print(table)
|
|
131
|
+
else:
|
|
132
|
+
console.print(" [yellow]no data files found[/yellow]")
|
|
133
|
+
|
|
134
|
+
console.print()
|
|
135
|
+
console.print(" [dim]press ctrl+c to stop[/dim]")
|
|
136
|
+
console.print()
|
|
137
|
+
|
|
138
|
+
# run server
|
|
139
|
+
try:
|
|
140
|
+
uvicorn.run(fastapi_app, host=host, port=port, log_level="warning")
|
|
141
|
+
except KeyboardInterrupt:
|
|
142
|
+
console.print("\n [dim]stopped[/dim]\n")
|
|
143
|
+
except OSError as e:
|
|
144
|
+
if "address already in use" in str(e).lower() or "10048" in str(e):
|
|
145
|
+
console.print(f"\n [red]error:[/red] port {port} already in use")
|
|
146
|
+
console.print(f" [dim]try: tc {path} -p {port + 1}[/dim]\n")
|
|
147
|
+
else:
|
|
148
|
+
console.print(f"\n [red]error:[/red] {e}\n")
|
|
149
|
+
sys.exit(1)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
if __name__ == "__main__":
|
|
153
|
+
main()
|
tinycontracts-0.1.1/README.md
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
# tinycontracts
|
|
2
|
-
|
|
3
|
-
Turn JSON, CSV, and Parquet files into REST APIs instantly.
|
|
4
|
-
|
|
5
|
-
## Install
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
pip install tinycontracts
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Usage
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
tc ./data
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
Or with options:
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
tc ./data --port 8000 --host 0.0.0.0
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## Example
|
|
24
|
-
|
|
25
|
-
Given this structure:
|
|
26
|
-
```
|
|
27
|
-
data/
|
|
28
|
-
users.json
|
|
29
|
-
orders.csv
|
|
30
|
-
config.json
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
You get:
|
|
34
|
-
```
|
|
35
|
-
GET /users # list all users
|
|
36
|
-
GET /users/1 # get user by id
|
|
37
|
-
GET /orders # list all orders
|
|
38
|
-
GET /config # get config
|
|
39
|
-
|
|
40
|
-
GET /_help # api documentation
|
|
41
|
-
GET /_schema # all schemas
|
|
42
|
-
GET /docs # swagger ui
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
## Filtering & Pagination
|
|
46
|
-
|
|
47
|
-
```bash
|
|
48
|
-
# filter by field
|
|
49
|
-
curl "localhost:4242/users?active=true"
|
|
50
|
-
|
|
51
|
-
# pagination
|
|
52
|
-
curl "localhost:4242/orders?_limit=10&_offset=20"
|
|
53
|
-
|
|
54
|
-
# sorting
|
|
55
|
-
curl "localhost:4242/orders?_sort=-created_at"
|
|
56
|
-
|
|
57
|
-
# combine
|
|
58
|
-
curl "localhost:4242/orders?status=pending&_limit=5&_sort=-amount"
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
## Schema
|
|
62
|
-
|
|
63
|
-
```bash
|
|
64
|
-
# all schemas
|
|
65
|
-
curl localhost:4242/_schema
|
|
66
|
-
|
|
67
|
-
# single resource schema
|
|
68
|
-
curl localhost:4242/users/_schema
|
|
69
|
-
```
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
# cli.py - typer entry point
|
|
2
|
-
import sys
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
|
|
5
|
-
import typer
|
|
6
|
-
import uvicorn
|
|
7
|
-
from rich.console import Console
|
|
8
|
-
from rich.panel import Panel
|
|
9
|
-
from rich.table import Table
|
|
10
|
-
|
|
11
|
-
from tinycontracts.server import create_app
|
|
12
|
-
|
|
13
|
-
console = Console()
|
|
14
|
-
|
|
15
|
-
app = typer.Typer(
|
|
16
|
-
name="tc",
|
|
17
|
-
help="serve a folder of json/csv/parquet files as a rest api",
|
|
18
|
-
no_args_is_help=True,
|
|
19
|
-
add_completion=False,
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def print_banner(host: str, port: int, resources: list[str], folder: Path):
|
|
24
|
-
# header
|
|
25
|
-
console.print()
|
|
26
|
-
console.print(
|
|
27
|
-
Panel.fit(
|
|
28
|
-
"[bold cyan]tinycontracts[/bold cyan] [dim]v0.1.1[/dim]",
|
|
29
|
-
border_style="cyan",
|
|
30
|
-
)
|
|
31
|
-
)
|
|
32
|
-
console.print()
|
|
33
|
-
|
|
34
|
-
# server info
|
|
35
|
-
console.print(f" [dim]folder:[/dim] {folder.resolve()}")
|
|
36
|
-
console.print(f" [dim]server:[/dim] [green]http://{host}:{port}[/green]")
|
|
37
|
-
console.print()
|
|
38
|
-
|
|
39
|
-
# endpoints table
|
|
40
|
-
if resources:
|
|
41
|
-
table = Table(show_header=True, header_style="bold", box=None, padding=(0, 2))
|
|
42
|
-
table.add_column("endpoint", style="green")
|
|
43
|
-
table.add_column("description", style="dim")
|
|
44
|
-
|
|
45
|
-
for name in sorted(resources):
|
|
46
|
-
table.add_row(f"/{name}", f"query {name}")
|
|
47
|
-
|
|
48
|
-
table.add_row("", "")
|
|
49
|
-
table.add_row("/docs", "swagger ui")
|
|
50
|
-
table.add_row("/_help", "api help")
|
|
51
|
-
|
|
52
|
-
console.print(table)
|
|
53
|
-
else:
|
|
54
|
-
console.print(" [yellow]no data files found[/yellow]")
|
|
55
|
-
|
|
56
|
-
console.print()
|
|
57
|
-
console.print(" [dim]press ctrl+c to stop[/dim]")
|
|
58
|
-
console.print()
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
@app.command()
|
|
62
|
-
def serve(
|
|
63
|
-
path: str = typer.Argument(..., help="folder containing data files"),
|
|
64
|
-
port: int = typer.Option(4242, "--port", "-p", help="port to serve on"),
|
|
65
|
-
host: str = typer.Option("127.0.0.1", "--host", "-H", help="host to bind to"),
|
|
66
|
-
):
|
|
67
|
-
"""serve data files as a rest api"""
|
|
68
|
-
folder = Path(path)
|
|
69
|
-
|
|
70
|
-
# validate folder exists
|
|
71
|
-
if not folder.exists():
|
|
72
|
-
console.print(f"\n [red]error:[/red] path not found: [bold]{folder}[/bold]")
|
|
73
|
-
console.print(f" [dim]make sure the path exists and try again[/dim]\n")
|
|
74
|
-
raise typer.Exit(1)
|
|
75
|
-
|
|
76
|
-
# validate it's a directory
|
|
77
|
-
if not folder.is_dir():
|
|
78
|
-
console.print(f"\n [red]error:[/red] not a folder: [bold]{folder}[/bold]")
|
|
79
|
-
console.print(f" [dim]tc needs a folder path, not a file[/dim]\n")
|
|
80
|
-
raise typer.Exit(1)
|
|
81
|
-
|
|
82
|
-
# check for data files
|
|
83
|
-
data_files = (
|
|
84
|
-
list(folder.glob("*.json"))
|
|
85
|
-
+ list(folder.glob("*.csv"))
|
|
86
|
-
+ list(folder.glob("*.parquet"))
|
|
87
|
-
)
|
|
88
|
-
if not data_files:
|
|
89
|
-
console.print(
|
|
90
|
-
f"\n [yellow]warning:[/yellow] no data files in [bold]{folder}[/bold]"
|
|
91
|
-
)
|
|
92
|
-
console.print("[dim]add .json, .csv, or .parquet files[/dim]\n")
|
|
93
|
-
|
|
94
|
-
# create app
|
|
95
|
-
try:
|
|
96
|
-
fastapi_app = create_app(folder)
|
|
97
|
-
resources = (
|
|
98
|
-
list(fastapi_app.state.resources.keys())
|
|
99
|
-
if hasattr(fastapi_app.state, "resources")
|
|
100
|
-
else []
|
|
101
|
-
)
|
|
102
|
-
except PermissionError:
|
|
103
|
-
console.print(f"\n [red]error:[/red] permission denied: [bold]{folder}[/bold]")
|
|
104
|
-
console.print("[dim]check file permissions and try again[/dim]\n")
|
|
105
|
-
raise typer.Exit(1)
|
|
106
|
-
except Exception as e:
|
|
107
|
-
console.print(f"\n [red]error:[/red] failed to load data: {e}\n")
|
|
108
|
-
raise typer.Exit(1)
|
|
109
|
-
|
|
110
|
-
# print startup banner
|
|
111
|
-
print_banner(host, port, resources, folder)
|
|
112
|
-
|
|
113
|
-
# run server
|
|
114
|
-
try:
|
|
115
|
-
uvicorn.run(fastapi_app, host=host, port=port, log_level="warning")
|
|
116
|
-
except KeyboardInterrupt:
|
|
117
|
-
console.print("\n [dim]stopped[/dim]\n")
|
|
118
|
-
except OSError as e:
|
|
119
|
-
if "address already in use" in str(e).lower() or "10048" in str(e):
|
|
120
|
-
console.print(f"\n [red]error:[/red] port {port} already in use")
|
|
121
|
-
console.print(f" [dim]try: tc {path} -p {port + 1}[/dim]\n")
|
|
122
|
-
else:
|
|
123
|
-
console.print(f"\n [red]error:[/red] {e}\n")
|
|
124
|
-
raise typer.Exit(1)
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
@app.command()
|
|
128
|
-
def version():
|
|
129
|
-
"""show version"""
|
|
130
|
-
console.print("tinycontracts [cyan]v0.1.1[/cyan]")
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
if __name__ == "__main__":
|
|
134
|
-
app()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|