Skip to content

Commit 9ff1d10

Browse files
authored
Modularize templates (#29)
* create new abstraction for code generation. create more clear documentation for each pynest project supported change "orm_config" file name convention to "config" add "setting.yml" file that contains metadata on the application * create 4 different examples apps: blank application mongo based application sync orm application (old versions of sqlalchemy) async orm applications (support in async await) * fix tests * add PyYAML to dependencies * fix integration_test.yaml * fix integration_test.yaml * add astor to dependencies * fix pynest command * fix pynest command * fix settings suffix * add black to dependencies * The server is started in the background using &. sleep 10 is used to wait for the server to start. You might need to adjust this time based on how quickly your server starts. After the tests, kill $(jobs -p) is used to terminate the background server process. || true is added to ensure that the workflow continues even if no job is found (in case the server process exits for some reason before this step). * add -f flag to the curl requests. fix the curl post request * fix post request * add sqlite integration test * fix prefix * use another port for the orm app * fix issue - #28 * change the integration test trigger rule * make --is_async a flag make integration test run parallel add explanation about the cli commands * fix Error: No such option: --is_async Did you mean --is-async? * fix curl: (7) Failed to connect to localhost port 80 after 0 ms: Connection refused * fix curl: (7) Failed to connect to localhost port 80 after 0 ms: Connection refused * fix curl: (7) Failed to connect to localhost port 80 after 0 ms: Connection refused * fix integration_test.yaml * fix integration_test.yaml * fix integration_test.yaml * fix integration_test.yaml * fix integration_test.yaml * fix documantation * update README.md * remove pynest logo background * remove pynest logo background * remove pynest logo background * update README.md
1 parent 61264bd commit 9ff1d10

File tree

156 files changed

+2462
-1287
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

156 files changed

+2462
-1287
lines changed
Lines changed: 58 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,65 @@
11
name: Integration Test
22

3-
on:
4-
workflow_dispatch:
5-
permissions:
6-
users:
7-
- ItayTheDar
3+
on: [push, workflow_call]
4+
85
jobs:
96
test:
107
runs-on: ubuntu-latest
118

9+
strategy:
10+
matrix:
11+
app_type: ["Blank", "SyncORM", "AsyncORM"]
12+
1213
steps:
13-
- name: Check out repository code
14-
uses: actions/checkout@v2
15-
16-
- name: Set up Python
17-
uses: actions/setup-python@v2
18-
with:
19-
python-version: '3.8' # or any version you need
20-
21-
- name: Install dependencies
22-
run: |
23-
python -m pip install --upgrade pip
24-
pip install .
25-
26-
- name: Start the application
27-
run: |
28-
pynest create-nest-app -n NestApp -db sqlite
29-
cd NestApp
30-
pynest generate-module -n user
31-
sudo python main.py &
32-
sleep 10 # Wait for the server to start
33-
34-
- name: Test the application
35-
run: |
36-
curl http://localhost:8000/docs
37-
curl get http://localhost:8000/get_user
38-
curl post http://localhost:8000/add_user -d '{"name": "test"}'
14+
- name: Check out repository code
15+
uses: actions/checkout@v2
16+
17+
- name: Set up Python
18+
uses: actions/setup-python@v2
19+
with:
20+
python-version: '3.8'
21+
22+
- name: Install dependencies
23+
run: |
24+
python -m pip install --upgrade pip
25+
pip install .
26+
27+
- name: Start Application
28+
run: |
29+
if [ "${{ matrix.app_type }}" == "Blank" ]; then
30+
app_name="NestApp"
31+
is_async=""
32+
elif [ "${{ matrix.app_type }}" == "SyncORM" ]; then
33+
app_name="ORMNestApp"
34+
is_async=""
35+
elif [ "${{ matrix.app_type }}" == "AsyncORM" ]; then
36+
app_name="AsyncORMNestApp"
37+
is_async="--is-async"
38+
fi
39+
40+
if [ "${{ matrix.app_type }}" == "Blank" ]; then
41+
pynest create-nest-app -n "$app_name"
42+
else
43+
pynest create-nest-app -n "$app_name" -db sqlite $is_async
44+
pip install aiosqlite
45+
fi
46+
47+
cd "$app_name"
48+
pynest g module -n user
49+
uvicorn "app:app" --host "0.0.0.0" --port 8000 --reload &
50+
51+
- name: Wait for the server to start
52+
run: sleep 10
53+
54+
- name: Test the application
55+
run: |
56+
curl -f http://localhost:8000/docs
57+
curl -f -X 'POST' \
58+
"http://localhost:8000/user/" \
59+
-H 'accept: application/json' \
60+
-H 'Content-Type: application/json' \
61+
-d '{"name": "Example Name"}'
62+
curl -f http://localhost:8000/user/
63+
64+
- name: Kill the server
65+
run: kill $(jobs -p) || true

README.md

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<p align="center">
2-
<img src="docs/imgs/pynest_new_logo_opt-removebg-preview.jpeg" title="pynest logo" width="300">
2+
<img src="docs/imgs/pynest-logo.png" title="pynest logo" width="300">
33
</p>
44
<p align="center">
55
<em>PyNest is a Python framework built on top of FastAPI that follows the modular architecture of NestJS</em>
@@ -26,7 +26,7 @@ PyNest is designed to help structure your APIs in an intuitive, easy to understa
2626

2727
With PyNest, you can build scalable and maintainable APIs with ease. The framework supports dependency injection, type annotations, decorators, and code generation, making it easy to write clean and testable code.
2828

29-
This framework is not a direct port of NestJS to Python but rather a re-imagining of the framework specifically for Python developers, including data scientists, data analysts, and data engineers. It aims to assist them in building better and faster APIs for their data applications.
29+
This framework is not a direct port of NestJS to Python but rather a re-imagining of the framework specifically for Python developers, including backend engineers and ML engineers. It aims to assist them in building better and faster APIs for their data applications.
3030

3131
## Getting Started
3232
To get started with PyNest, you'll need to install it using pip:
@@ -44,17 +44,12 @@ this command will create a new project with the following structure:
4444

4545
```text
4646
├── app.py
47-
├── orm_config.py
4847
├── main.py
48+
├── requirements.txt
49+
├── .gitignore
50+
├── README.md
4951
├── src
5052
│ ├── __init__.py
51-
│ ├── examples
52-
│ │ ├── __init__.py
53-
│ │ ├── examples_controller.py
54-
│ │ ├── examples_service.py
55-
│ │ ├── examples_model.py
56-
│ ├── ├── examples_entity.py
57-
│ ├── ├── examples_module.py
5853
```
5954

6055
once you have created your app, get into the folder and run the following command:
@@ -66,20 +61,20 @@ cd my_app_name
6661
run the server with the following command:
6762

6863
```bash
69-
uvicorn "app:app" --host "0.0.0.0" --port "80" --reload
64+
uvicorn "app:app" --host "0.0.0.0" --port "8000" --reload
7065
```
7166

72-
Now you can visit [OpenAPI](http://localhost:80/docs) in your browser to see the default API documentation.
67+
Now you can visit [OpenAPI](http://localhost:8000/docs) in your browser to see the default API documentation.
7368

7469
### Adding modules
7570

7671
To add a new module to your application, you can use the pynest generate module command:
77-
72+
]()
7873
```bash
79-
pynest generate-module -n users
74+
pynest g module -n users
8075
```
8176

82-
This will create a new module called ```users``` in your application with the following structure:
77+
This will create a new module called ```users``` in your application with the following structure under the ```src``` folder:
8378

8479
```text
8580
├── users
@@ -97,6 +92,42 @@ You can then start defining routes and other application components using decora
9792

9893
For more information on how to use PyNest, check out the official documentation at https://pythonnest.github.io/PyNest/.
9994

95+
## PyNest CLI Usage Guide
96+
97+
This document provides a guide on how to use the PyNest Command Line Interface (CLI). Below are the available commands and their descriptions:
98+
99+
### `pynest` Command
100+
101+
- **Description**: The main command group for PyNest CLI.
102+
103+
#### `create-nest-app` Subcommand
104+
105+
- **Description**: Create a new nest app.
106+
- **Options**:
107+
- `--app-name`/`-n`: The name of the nest app (required).
108+
- `--db-type`/`-db`: The type of the database (optional). You can specify PostgreSQL, MySQL, SQLite, or MongoDB.
109+
- `--is-async`: Whether the project should be asynchronous (optional, default is False).
110+
111+
### `g` command group
112+
113+
- **Description**: Group command for generating boilerplate code.
114+
115+
#### `module` Subcommand
116+
117+
- **Description**: Generate a new module (controller, service, entity, model, module).
118+
- **Options**:
119+
- `--name`/`-n`: The name of the module (required).
120+
121+
122+
#### CLI Examples
123+
* create a blank nest application -
124+
`pynest create-nest-app -n my_app_name`
125+
* create a nest application with postgres database and async connection -
126+
<br>
127+
`pynest create-nest-app -n my_app_name -db postgresql --is-async`
128+
* create new module -
129+
`pynest g module -n users`
130+
100131

101132
## Key Features
102133
### Modular Architecture

docs/async_orm.md

Lines changed: 46 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ environment.
1010

1111
- Python 3.9+
1212
- PyNest (latest version)
13-
- SQLAlchemy 2.0
13+
- SQLAlchemy < 2.0
14+
- async driver for your database (e.g. asyncpg for PostgreSQL, aiomysql for MySQL, or aiosqlite for SQLite)
1415

1516
## Setting Up
1617

@@ -27,14 +28,20 @@ pip install pynest-api
2728
#### Create a new project
2829

2930
```bash
30-
pynest create-nest-app -n my_app_name -db postgresql --async
31+
pynest create-nest-app -n my_app_name -db postgresql --is-async
32+
```
33+
34+
Note: you need to install the async driver for your database, for example, if you are using PostgreSQL, you need to install asyncpg:
35+
36+
```bash
37+
pip install asyncpg
3138
```
3239

3340
this command will create a new project with the following structure:
3441

3542
```text
3643
├── app.py
37-
├── orm_config.py
44+
├── config.py
3845
├── main.py
3946
├── src
4047
│ ├── __init__.py
@@ -49,7 +56,7 @@ this command will create a new project with the following structure:
4956

5057
once you have created your app, this is the code that support the asynchronous feature:
5158

52-
`orm_config.py`
59+
`config.py`
5360

5461
```python
5562
from nest.core.database.orm_provider import AsyncOrmProvider
@@ -77,9 +84,9 @@ Now we need to declare the App object and register the module in
7784
`app.py`
7885

7986
```python
80-
from orm_config import config
87+
from config import config
8188
from nest.core.app import App
82-
from src.examples.examples_module import ExamplesModule
89+
from .examples_module import ExamplesModule
8390

8491
app = App(
8592
description="PyNest service",
@@ -101,24 +108,14 @@ AsyncOrmProvider is a key component in managing asynchronous database connection
101108
### AsyncSession
102109
AsyncSession from sqlalchemy.ext.asyncio is used for executing asynchronous database operations. It is essential for leveraging the full capabilities of SQLAlchemy 2.0 in an async environment.
103110

104-
## Implementing Async Features
105-
### Creating Models
106-
Define your models using SQLAlchemy's declarative base. For example, the Examples model:
107-
108-
### AsyncSession
109-
110-
AsyncSession, from sqlalchemy.ext.asyncio is used
111-
for executing asynchronous database operations.It is essential for leveraging the full capabilities of SQLAlchemy 2.0 in
112-
an async environment.
113-
114111
## Implementing Async Features
115112

116-
### Creating Models
113+
### Creating Entities
117114

118115
Define your models using SQLAlchemy's declarative base. For example, the Examples model:
119116

120117
```python
121-
from orm_config import config
118+
from config import config
122119
from sqlalchemy import Integer, String
123120
from sqlalchemy.orm import Mapped, mapped_column
124121

@@ -136,11 +133,11 @@ Implement services to handle business logic.
136133
There are two ways of creating service.
137134

138135
1. In that way, the service does not init any parameter, and that each function that depends on the database is getting
139-
the async session fron the controller
136+
the async session from the controller
140137

141138
```python
142-
from src.examples.examples_model import Examples
143-
from src.examples.examples_entity import Examples as ExamplesEntity
139+
from .examples_model import Examples
140+
from .examples_entity import Examples as ExamplesEntity
144141
from nest.core.decorators.database import async_db_request_handler
145142
from sqlalchemy import select
146143
from sqlalchemy.ext.asyncio import AsyncSession
@@ -168,9 +165,9 @@ class ExamplesService:
168165
using the session that was init in the constructor
169166

170167
```python
171-
from src.examples.examples_model import Examples
172-
from src.examples.examples_entity import Examples as ExamplesEntity
173-
from orm_config import config
168+
from .examples_model import Examples
169+
from .examples_entity import Examples as ExamplesEntity
170+
from config import config
174171
from nest.core.decorators.database import async_db_request_handler
175172
from functools import lru_cache
176173
from sqlalchemy import select, text
@@ -181,7 +178,7 @@ class ExamplesService:
181178

182179
def __init__(self):
183180
self.orm_config = config
184-
self.session = self.orm_config.get_self_db
181+
self.session = self.orm_config.session
185182

186183
@async_db_request_handler
187184
async def add_examples(self, examples: Examples):
@@ -206,18 +203,18 @@ logic.
206203

207204
Here we have also two ways of creating the controller.
208205

209-
1. In that way, the controller's functions are getting the async session from the orm_config
206+
1. In that way, the controller's functions are getting the async session from the config
210207

211208
```python
212209
from nest.core import Controller, Get, Post, Depends
213210

214-
from src.examples.examples_service import ExamplesService
215-
from src.examples.examples_model import Examples
216-
from orm_config import config
211+
from .examples_service import ExamplesService
212+
from .examples_model import Examples
213+
from config import config
217214
from sqlalchemy.ext.asyncio import AsyncSession
218215

219216

220-
@Controller("examples", prefix="examples")
217+
@Controller("examples")
221218
class ExamplesController:
222219
service: ExamplesService = Depends(ExamplesService)
223220

@@ -236,11 +233,11 @@ class ExamplesController:
236233
```python
237234
from nest.core import Controller, Get, Post, Depends
238235

239-
from src.examples.examples_service import ExamplesService
240-
from src.examples.examples_model import Examples
236+
from .examples_service import ExamplesService
237+
from .examples_model import Examples
241238

242239

243-
@Controller("examples", prefix="examples")
240+
@Controller("examples")
244241
class ExamplesController:
245242
service: ExamplesService = Depends(ExamplesService)
246243

@@ -256,6 +253,22 @@ class ExamplesController:
256253
> **Hint:** Keep in mind that there are no difference between the two methods, the only difference is the way of getting
257254
> the async session object, and how to use it. Choose you favorite syntax and use it.
258255
256+
### Creating Module
257+
258+
Create a module to register the controller and the service.
259+
260+
```python
261+
from .examples_controller import ExamplesController
262+
from .examples_service import ExamplesService
263+
264+
265+
class ExamplesModule:
266+
controllers = [ExamplesController]
267+
services = [ExamplesService]
268+
```
269+
270+
271+
259272
## async_db_request_handler decorator
260273

261274
The async_db_request_handler decorator is used to handle the async session object. It is used in the service layer to

0 commit comments

Comments
 (0)