My first Python web framework.
Project description
PyGenUz: Python Web Framewrok build for learning purposes
PyGenUz is a Python web framewrok built for learning purposes.
It's a WSGI framework and can be used with any WSGI aplication server such as Gunicorn.
Installation
pip install pygenuz
How to use it
Basic usage
```
from pygenuz.app import PyGenUz
app = PyGenUz()
@app.route('/home', allowed_methods="get")
def home(request, response):
response.text = "Hello from home page"
@app.route('/about')
def about(request, response):
response.text = "Hello from about page"
@app.route("/hello/{name}")
def greeting(request, response, name):
response.text = f"Hello, {name}"
@app.route('/books')
class Books:
def get(self, request, response):
response.text = "Books page"
def post(self, request, response):
response.text = "endpoint to create a book"
def delete(self, request, response):
response.text = "deleted"
```
How we can use Templates?
Using Templates
We have to make dir name called temps/
@app.route("/temp")
def template_handler(req, resp):
resp.html = app.template(
"home.html",
context = {"new_title": "New title", "new_body": "New body"}
)
How we can use json?
@app.route("/json")
def json_handler(req, resp):
response_data = {"name": "some name"}
resp.json = response_data
How we can use Static files
We have to make dir name called static/
.html
<link rel="stylesheet" href="/static/test.css">
Unit Tests
Here is hard code of Unit Tests
import pytest
from pygenuz.middelware import Middleware
def test_basic_route_adding(app):
@app.route("/home")
def home(req, resp):
resp.text = "Hello from Home"
def test_duplicate_routes_throws_exception(app):
@app.route("/home")
def home(req, resp):
resp.text = "Hello from Home"
with pytest.raises(AssertionError):
@app.route("/home")
def home2(req, resp):
resp.text = "Hello from Home"
def test_requests_can_be_send_by_test_client(app, test_client):
@app.route("/home")
def home(req, resp):
resp.text = "Hello from Home"
response = test_client.get("http://testserver/home")
assert response.text == "Hello from Home"
def test_parameterized_routing(app, test_client):
@app.route("/hello/{name}")
def greeting(request, response, name):
response.text = f"Hello, {name}"
assert test_client.get("http://testserver/hello/Ezozbek").text == "Hello, Ezozbek"
assert test_client.get("http://testserver/hello/Bob").text == "Hello, Bob"
def test_default_response(test_client):
response = test_client.get("http://testserver/nonexsistent")
assert response.text == "Not Found."
assert response.status_code == 404
def test_class_based_get(app, test_client):
@app.route("/books")
class Books:
def get(self, req, resp):
resp.text = "Books page"
assert test_client.get("http://testserver/books").text == "Books page"
def test_class_based_post(app, test_client):
@app.route("/books")
class Books:
def post(self, req, resp):
resp.text = "endpoint to create a book"
assert test_client.post("http://testserver/books").text == "endpoint to create a book"
def test_class_based_method_not_allowed(app, test_client):
@app.route("/books")
class Books:
def post(self, req, resp):
resp.text = "endpoint to create a book"
response = test_client.get("http://testserver/books")
assert response.text == "Method Not Allowed"
assert response.status_code == 405
def test_alternative_router_adding(app, test_client):
def new_handler(req, resp):
resp.text = "From new handler"
app.add_route('/newhandler', new_handler)
assert test_client.get("http://testserver/newhandler").text == "From new handler"
def test_template_handler(app,test_client):
@app.route("/temp")
def template(req, resp):
resp.body = app.template(
"home.html",
context = {"new_title": "Best title", "new_body": "Best body"}
)
response = test_client.get("http://testserver/temp")
print(response.headers)
assert "Best title" in response.text
assert "Best body" in response.text
assert "text/html" in response.headers["Content-Type"]
def test_custom_exception_handler(app, test_client):
def on_exception(req, resp, exc):
resp.text = "Something bad happened"
app.add_exception_handler(on_exception)
@app.route("/exception")
def exeption_throwing_handler(req, resp):
raise AttributeError("some error")
response = test_client.get("http://testserver/exception")
assert response.text == "Something bad happened"
def test_non_existent_static_file(test_client):
assert test_client.get("http://testserver/nonexsistent.css").status_code == 404
def test_serving_static_file(test_client):
response = test_client.get("http://testserver/static/test.css")
assert response.text == "body {background-color: chocolate;}"
def test_middleware_methods_are_called(app, test_client):
process_request_called = False
process_response_called = False
class SimpleMiddleware(Middleware):
def __init__(self, app):
super().__init__(app)
def process_request(self, req):
nonlocal process_request_called
process_request_called = True
def process_response(self, req, resp):
nonlocal process_response_called
process_response_called = True
app.add_middleware(SimpleMiddleware)
@app.route("/home")
def index(req, resp):
resp.text = "Hello from home page"
test_client.get("http://testserver/home")
assert process_request_called is True
assert process_response_called is True
def test_allowed_methods_for_function_based_handlers(app, test_client):
@app.route("/home", allowed_methods=["post"])
def home(req, resp):
resp.text = "Hello from home"
resp = test_client.get("http://testserver/home")
assert resp.status_code == 405
assert resp.text == "Method Not Allowed"
def test_json_response_helper(app, test_client):
@app.route("/json")
def json_handler(req, resp):
resp.json = {"name": "Ezozbek"}
resp = test_client.get("http://testserver/json")
# Print out response content and headers for debugging
print("Response Content:", resp.content)
print("Response Headers:", resp.headers)
resp_data = resp.json() # Corrected line
assert resp.headers["Content-Type"] == "application/json"
assert resp_data["name"] == "Ezozbek"
def test_text_response_helper(app, test_client):
@app.route("/text")
def text_handler(req, resp):
resp.text = "plain text"
response = test_client.get("http://testserver/text")
assert "text/plain" in response.headers["Content-Type"]
assert response.text == "plain text"
def test_html_response_helper(app, test_client):
@app.route("/html")
def html_handler(req, resp):
resp.body = app.template(
"home.html",
context = {"new_title": "Best title", "new_body": "Best body"}
)
response = test_client.get("http://testserver/html")
assert "text/html" in response.headers["Content-Type"]
assert "Best title" in response.text
assert "Best body" in response.text
MODELS AND ORM
For create models you have to make only models.py and in models.py you must to write the following code.
from pygenuz.db import *
Base = declarative_base()
Example models.py
from pygenuz.db import *
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
username = Column(String(50), nullable=False)
email = Column(String(100), nullable=False, unique=True)
age = Column(Integer)
is_active = Column(Boolean, default=True)
class Book(Base):
__tablename__ = 'books'
id = Column(Integer, primary_key=True)
title = Column(String(100), nullable=False)
author = Column(String(100), nullable=False)
published_date = Column(DateTime)
user_id = Column(Integer, ForeignKey('users.id'))
user = relationship('User', back_populates='books')
After writing models.py, you must to write configure.py to set the database settings
from pygenuz.configs import *
from models import Base
def configure_database():
if Base:
DATABASE_URL = 'sqlite:///database.db'
engine = create_engine(DATABASE_URL)
Session = sessionmaker(bind=engine)
session = Session()
Base.metadata.create_all(engine)
return session
else:
return None
if __name__ == "__main__":
configure_database()
After writing configure.py, you need to copy your models to the database. To do this, you need to write a command in the terminal or cmd
python configure.py
will return you a Migrated response if successful else error
Using models.py in your views.py or main.py, you must import these codes.
from pygenuz.app import PyGenUz
from configure import *
app = PyGenUz()
session = configure_database()
List in jinja
@app.route("/books", allowed_methods=["get"])
def list_books(request, response):
books = session.query(Book).all()
response.html = app.template(
"books.html",
context={"books": books}
)
books.html file
<h1>Book List</h1>
<table>
<thead>
<tr>
<th>Title</th>
<th>Author</th>
<th>Published Date</th>
</tr>
</thead>
<tbody>
{% for book in books %}
<tr>
<td>{{ book.title }}</td>
<td>{{ book.author }}</td>
<td>{{ book.published_date }}</td>
</tr>
{% endfor %}
</tbody>
</table>
Get json data
@app.route("/api/books", allowed_methods=["get"])
def get_books(request, response):
session = configure_database()
books = session.query(Book).all()
books_data = [
{
"id": book.id,
"title": book.title,
"author": book.author,
"published_date": str(book.published_date)
}
for book in books
]
response.json = books_data
POST json data
@app.route("/api/book", allowed_methods=["post"])
def create_book(request, response):
session = configure_database()
book_data = request.json
new_book = Book(
title=book_data.get('title'),
author=book_data.get('author'),
published_date=book_data.get('published_date')
)
session.add(new_book)
session.commit()
response.text = "Book created successfully"
UPDATE and DELETE json data
@app.route("/api/books/{book_id}", allowed_methods=["put", "delete"])
def book_api(request, response, book_id):
if request.method == "PUT":
update_book(request, response, book_id)
elif request.method == "DELETE":
delete_book(request, response, book_id)
def update_book(request, response, book_id):
book_data = request.json
book = session.query(Book).filter_by(id=book_id).first()
if not book:
response.text = f"Book with ID {book_id} not found"
response.status_code = 404
return
book.title = book_data.get('title')
book.author = book_data.get('author')
book.published_date = book_data.get('published_date')
session.commit()
response.text = "Book updated successfully"
def delete_book(request, response, book_id):
book = session.query(Book).filter_by(id=book_id).first()
if not book:
response.text = f"Book with ID {book_id} not found"
response.status_code = 404
return
# Delete the book
session.delete(book)
session.commit()
response.text = "Book deleted successfully"
EVERY MONDAY YOU WILL SEE A NEW BIG UPDATE
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
pygenuz-0.1.9.tar.gz
(10.7 kB
view details)
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pygenuz-0.1.9.tar.gz.
File metadata
- Download URL: pygenuz-0.1.9.tar.gz
- Upload date:
- Size: 10.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.0.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c876e29a8f05f546d08c05d6cdd2d4e071c22aabc75d9148ba0382e9c8eeb333
|
|
| MD5 |
5a8af2c2644f567c7f718adbc902f421
|
|
| BLAKE2b-256 |
6e68aec7d26e6d64ba02b6e22483d6e77622bad74a3f534c09a73bab2d7b60f2
|
File details
Details for the file pygenuz-0.1.9-py2.py3-none-any.whl.
File metadata
- Download URL: pygenuz-0.1.9-py2.py3-none-any.whl
- Upload date:
- Size: 7.3 kB
- Tags: Python 2, Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.0.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
687417e0d6d5b81dadf91515598dac9ffc31899423018d41a120148dbf3f920b
|
|
| MD5 |
de1346ebd0ff36979c03dd279830f077
|
|
| BLAKE2b-256 |
937b3a8e718da3aaa8a69a3e7434e9fc1e8045bf57a5ec742c6d72ea81b726ab
|