tinycontracts 0.1.1__py3-none-any.whl → 0.1.2__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.
tinycontracts/cli.py CHANGED
@@ -1,42 +1,120 @@
1
- # cli.py - typer entry point
1
+ # cli.py - fast startup with lazy imports
2
2
  import sys
3
- from pathlib import Path
4
3
 
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
4
 
11
- from tinycontracts.server import create_app
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
12
56
 
13
- console = Console()
57
+ console = Console()
58
+ folder = Path(path)
14
59
 
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
- )
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)
21
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
22
104
 
23
- def print_banner(host: str, port: int, resources: list[str], folder: Path):
24
- # header
25
105
  console.print()
26
106
  console.print(
27
107
  Panel.fit(
28
- "[bold cyan]tinycontracts[/bold cyan] [dim]v0.1.1[/dim]",
108
+ "[bold cyan]tinycontracts[/bold cyan] [dim]v0.1.2[/dim]",
29
109
  border_style="cyan",
30
110
  )
31
111
  )
32
112
  console.print()
33
113
 
34
- # server info
35
114
  console.print(f" [dim]folder:[/dim] {folder.resolve()}")
36
115
  console.print(f" [dim]server:[/dim] [green]http://{host}:{port}[/green]")
37
116
  console.print()
38
117
 
39
- # endpoints table
40
118
  if resources:
41
119
  table = Table(show_header=True, header_style="bold", box=None, padding=(0, 2))
42
120
  table.add_column("endpoint", style="green")
@@ -57,59 +135,6 @@ def print_banner(host: str, port: int, resources: list[str], folder: Path):
57
135
  console.print(" [dim]press ctrl+c to stop[/dim]")
58
136
  console.print()
59
137
 
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
138
  # run server
114
139
  try:
115
140
  uvicorn.run(fastapi_app, host=host, port=port, log_level="warning")
@@ -121,14 +146,8 @@ def serve(
121
146
  console.print(f" [dim]try: tc {path} -p {port + 1}[/dim]\n")
122
147
  else:
123
148
  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]")
149
+ sys.exit(1)
131
150
 
132
151
 
133
152
  if __name__ == "__main__":
134
- app()
153
+ main()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tinycontracts
3
- Version: 0.1.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, and Parquet files into REST APIs instantly.
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
- Or with options:
41
+ That's it. Your files are now API endpoints.
42
+
43
+ ## Options
42
44
 
43
45
  ```bash
44
- tc ./data --port 8000 --host 0.0.0.0
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
- ## Filtering & Pagination
60
+ ## Querying
70
61
 
71
62
  ```bash
72
- # filter by field
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
- # sorting
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&_sort=-amount"
73
+ curl "localhost:4242/orders?status=pending&_limit=5"
83
74
  ```
84
75
 
85
- ## Schema
76
+ ## Endpoints
86
77
 
87
- ```bash
88
- # all schemas
89
- curl localhost:4242/_schema
90
-
91
- # single resource schema
92
- curl localhost:4242/users/_schema
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 |
@@ -1,10 +1,10 @@
1
1
  tinycontracts/__init__.py,sha256=WEsinRwkR1RI9izRzpTB0MidHLvvZxFE0S6KvkcK2aE,17
2
- tinycontracts/cli.py,sha256=rZL3uqxMBCoIoowgIWotqZgayPr7i2MnZEAICFLyIM0,4078
2
+ tinycontracts/cli.py,sha256=6Vhu0BLU2aC89yTlwl17qFJxYnOilSf79OuivCNo4Cs,4620
3
3
  tinycontracts/loader.py,sha256=CYgvvdTmuioaQeaTZNxLKy0ndFaCjR-xzQoh61FN5q4,884
4
4
  tinycontracts/routes.py,sha256=ZVtr7-Sx1IAJIlK9yHgiFzgUa5Wpodsm90jliopQagM,3840
5
5
  tinycontracts/schema.py,sha256=tZjLLvQGKi5H4drZZRNCDWY5SWg4_MnYtJWz5hsw8ng,692
6
6
  tinycontracts/server.py,sha256=23gJheNqjPHckSeNlquG-G8viJNrMEVItcYxBg-n-t8,728
7
- tinycontracts-0.1.1.dist-info/METADATA,sha256=0BC5D-yzLu6lwCSsOeobB-oSHBSFYWKGOOalDzCRCzk,1871
8
- tinycontracts-0.1.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
9
- tinycontracts-0.1.1.dist-info/entry_points.txt,sha256=mBH0FJ35wm3PDzo0sK-0XP0NA53XQ93JpMCYXRBEPiI,45
10
- tinycontracts-0.1.1.dist-info/RECORD,,
7
+ tinycontracts-0.1.2.dist-info/METADATA,sha256=XqFjt4-dRfQU3gbE_sMAiUmBdRUfTsxNDT4dIDlI_gQ,1901
8
+ tinycontracts-0.1.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
9
+ tinycontracts-0.1.2.dist-info/entry_points.txt,sha256=6hAX2IWHaA63CPW3iJVFSB5t9461mefFZ72LQz2unWw,46
10
+ tinycontracts-0.1.2.dist-info/RECORD,,
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ tc = tinycontracts.cli:main
@@ -1,2 +0,0 @@
1
- [console_scripts]
2
- tc = tinycontracts.cli:app