{"id":43051198,"url":"https://github.com/rahmonov/alcazar","last_synced_at":"2026-01-31T10:36:24.092Z","repository":{"id":41465434,"uuid":"169835427","full_name":"rahmonov/alcazar","owner":"rahmonov","description":"Python Web Framework built for learning purposes","archived":false,"fork":false,"pushed_at":"2021-03-20T00:36:44.000Z","size":121,"stargazers_count":238,"open_issues_count":5,"forks_count":35,"subscribers_count":12,"default_branch":"master","last_synced_at":"2025-09-30T01:24:56.401Z","etag":null,"topics":["python"],"latest_commit_sha":null,"homepage":"http://rahmonov.me/posts/write-python-framework-part-one/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rahmonov.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-02-09T05:30:04.000Z","updated_at":"2025-09-25T11:37:34.000Z","dependencies_parsed_at":"2022-08-12T08:40:07.741Z","dependency_job_id":null,"html_url":"https://github.com/rahmonov/alcazar","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/rahmonov/alcazar","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rahmonov%2Falcazar","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rahmonov%2Falcazar/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rahmonov%2Falcazar/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rahmonov%2Falcazar/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rahmonov","download_url":"https://codeload.github.com/rahmonov/alcazar/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rahmonov%2Falcazar/sbom","scorecard":{"id":758984,"data":{"date":"2025-08-11","repo":{"name":"github.com/rahmonov/alcazar","commit":"2979b3d38ccb544e704a979f67dc184f0a492ee8"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":1.3,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":0,"reason":"license file not detected","details":["Warn: project does not have a license file"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":0,"reason":"12 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-hc5x-x2vx-497g","Warn: Project is vulnerable to: GHSA-w3h3-4rj7-4ph4","Warn: Project is vulnerable to: PYSEC-2019-217 / GHSA-462w-v97r-4m45","Warn: Project is vulnerable to: GHSA-cpwx-vrp4-4pq7","Warn: Project is vulnerable to: PYSEC-2021-66 / GHSA-g3rq-g295-4j3m","Warn: Project is vulnerable to: GHSA-h5c8-rqwp-cp95","Warn: Project is vulnerable to: GHSA-h75v-3vvj-5mfj","Warn: Project is vulnerable to: GHSA-q2x7-8rv6-6q7h","Warn: Project is vulnerable to: GHSA-9hjg-9r4m-mvj7","Warn: Project is vulnerable to: GHSA-9wx4-h78v-vm56","Warn: Project is vulnerable to: PYSEC-2023-74 / GHSA-j8r2-6x86-q33q","Warn: Project is vulnerable to: PYSEC-2024-188 / GHSA-mg3v-6m49-jhp3"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-22T22:42:25.674Z","repository_id":41465434,"created_at":"2025-08-22T22:42:25.674Z","updated_at":"2025-08-22T22:42:25.674Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28938770,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-31T10:18:23.202Z","status":"ssl_error","status_checked_at":"2026-01-31T10:18:22.693Z","response_time":128,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["python"],"created_at":"2026-01-31T10:36:22.307Z","updated_at":"2026-01-31T10:36:24.087Z","avatar_url":"https://github.com/rahmonov.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n    \u003cimg src=\"https://github.com/rahmonov/alcazar/blob/master/alcazar.jpg?raw=True\"\u003e\n\u003c/p\u003e\n\n---\n\n![purpose](https://img.shields.io/badge/purpose-learning-green.svg)\n[![travis](https://travis-ci.org/rahmonov/alcazar.svg?branch=master)](https://travis-ci.org/rahmonov/alcazar)\n\n# Alcazar\n\nAlcazar is a Python Web Framework built for learning purposes. The plan is to learn how frameworks are built by implementing their features,\nwriting blog posts about them and keeping the codebase as simple as possible.\n\nIt is a WSGI framework and can be used with any WSGI application server such as Gunicorn.\n\n## Inspiration\n\nI was inspired to make a web framework after reading [Florimond Monca](https://twitter.com/FlorimondManca)'s [blog post](https://blog.florimondmanca.com/how-i-built-a-web-framework-and-became-an-open-source-maintainer)\nabout how he built a web framework and became an open source maintainer. He wrote about how thrilling the experience has been for him so I decided I would give it a try as well.\nThank you, [Florimond](https://github.com/florimondmanca) and of course [Kenneth Reitz](https://twitter.com/kennethreitz) who in turn inspired Florimond to write a framework with\nhis own framework [Responder](https://github.com/kennethreitz/responder). Go check out both [Bocadillo by Florimond Monca](https://github.com/bocadilloproject/bocadillo) and [Responder by Kenneth Reitz](https://github.com/kennethreitz/responder).\nIf you like them, show some love by staring their repos.\n\n## Blog posts\n\n- [Part I: Intro, API, request handlers, routing (both simple and parameterized)](http://rahmonov.me/posts/write-python-framework-part-one/)\n- [Part II: class based handlers, route overlap check, unit tests](http://rahmonov.me/posts/write-python-framework-part-two/)\n- [Part III: templates support, test client, django way of adding routes](http://rahmonov.me/posts/write-python-framework-part-three/)\n- [Part IV: custom exception handler, support for static files, middleware](http://rahmonov.me/posts/write-python-framework-part-four/)\n\n## Quick Start\n\nInstall it:\n\n```bash\npip install alcazar-web-framework\n```\n\nBasic Usage:\n\n```python\n# app.py\nfrom alcazar import Alcazar\n\napp = Alcazar()\n\n\n@app.route(\"/\")\ndef home(req, resp):\n    resp.text = \"Hello, this is a home page.\"\n\n\n@app.route(\"/about\")\ndef about_page(req, resp):\n    resp.text = \"Hello, this is an about page.\"\n\n\n@app.route(\"/{age:d}\")\ndef tell_age(req, resp, age):\n    resp.text = f\"Your age is {age}\"\n\n\n@app.route(\"/{name:l}\")\nclass GreetingHandler:\n    def get(self, req, resp, name):\n        resp.text = f\"Hello, {name}\"\n\n\n@app.route(\"/show/template\")\ndef handler_with_template(req, resp):\n    resp.html = app.template(\"example.html\", context={\"title\": \"Awesome Framework\", \"body\": \"welcome to the future!\"})\n\n\n@app.route(\"/json\")\ndef json_handler(req, resp):\n    resp.json = {\"this\": \"is JSON\"}\n\n\n@app.route(\"/custom\")\ndef custom_response(req, resp):\n    resp.body = b'any other body'\n    resp.content_type = \"text/plain\"\n```\n\nStart:\n\n```bash\ngunicorn app:app\n```\n\n## Handlers\n\nIf you use class based handlers, only the methods that you implement will be allowed:\n\n```python\n@app.route(\"/{name:l}\")\nclass GreetingHandler:\n    def get(self, req, resp, name):\n        resp.text = f\"Hello, {name}\"\n```\n\nThis handler will only allow `GET` requests. That is, `POST` and others will be rejected. The same thing can be done with\nfunction based handlers in the following way:\n\n```python\n@app.route(\"/\", methods=[\"get\"])\ndef home(req, resp):\n    resp.text = \"Hello, this is a home page.\"\n```\n\nNote that if you specify `methods` for class based handlers, they will be ignored.\n\n## Unit Tests\n\nThe recommended way of writing unit tests is with [pytest](https://docs.pytest.org/en/latest/). There are two built in fixtures\nthat you may want to use when writing unit tests with Alcazar. The first one is `app` which is an instance of the main `Alcazar` class:\n\n```python\ndef test_route_overlap_throws_exception(app):\n    @app.route(\"/\")\n    def home(req, resp):\n        resp.text = \"Welcome Home.\"\n\n    with pytest.raises(AssertionError):\n        @app.route(\"/\")\n        def home2(req, resp):\n            resp.text = \"Welcome Home2.\"\n```\n\nThe other one is `client` that you can use to send HTTP requests to your handlers. It is based on the famous [requests](http://docs.python-requests.org/en/master/) and it should feel very familiar:\n\n```python\ndef test_parameterized_route(app, client):\n    @app.route(\"/{name}\")\n    def hello(req, resp, name):\n        resp.text = f\"hey {name}\"\n\n    assert client.get(url(\"/matthew\")).text == \"hey matthew\"\n```\n\nNote that there is a `url()` function used. It is used to generate the absolute url of the request given a relative url. Import it before usage:\n\n```python\nfrom alcazar.utils.tests import url\n```\n\n## Templates\n\nThe default folder for templates is `templates`. You can change it when initializing the main `Alcazar()` class:\n\n```python\napp = Alcazar(templates_dir=\"templates_dir_name\")\n```\n\nThen you can use HTML files in that folder like so in a handler:\n\n```python\n@app.route(\"/show/template\")\ndef handler_with_template(req, resp):\n    resp.html = app.template(\"example.html\", context={\"title\": \"Awesome Framework\", \"body\": \"welcome to the future!\"})\n```\n\n## Static Files\n\nJust like templates, the default folder for static files is `static` and you can override it:\n\n```python\napp = Alcazar(static_dir=\"static_dir_name\")\n```\n\nThen you can use the files inside this folder in HTML files:\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml lang=\"en\"\u003e\n\n\u003chead\u003e\n  \u003cmeta charset=\"UTF-8\"\u003e\n  \u003ctitle\u003e{{title}}\u003c/title\u003e\n\n  \u003clink href=\"/static/main.css\" rel=\"stylesheet\" type=\"text/css\"\u003e\n\u003c/head\u003e\n\n\u003cbody\u003e\n    \u003ch1\u003e{{body}}\u003c/h1\u003e\n    \u003cp\u003eThis is a paragraph\u003c/p\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n## Custom Exception Handler\n\nSometimes, depending on the exception raised, you may want to do a certain action. For such cases, you can register an exception handler:\n\n```python\ndef on_exception(req, resp, exception):\n    if isinstance(exception, HTTPError):\n        if exception.status == 404:\n            resp.text = \"Unfortunately the thing you were looking for was not found\"\n        else:\n            resp.text = str(exception)\n    else:\n        # unexpected exceptions\n        if app.debug:\n            debug_exception_handler(req, resp, exception)\n        else:\n            print(\"These unexpected exceptions should be logged.\")\n\napp = Alcazar(debug=False)\napp.add_exception_handler(on_exception)\n```\n\nThis exception handler will catch 404 HTTPErrors and change the text to `\"Unfortunately the thing you were looking for was not found\"`. For other HTTPErrors, it will simply\nshow the exception message. If the raised exception is not an HTTPError and if `debug` is set to True, it will show the exception and its traceback. Otherwise, it will log it.\n\n## Middleware\n\nYou can create custom middleware classes by inheriting from the `alcazar.middleware.Middleware` class and override its two methods\nthat are called before and after each request:\n\n```python\nfrom alcazar import Alcazar\nfrom alcazar.middleware import Middleware\n\napp = Alcazar()\n\n\nclass SimpleCustomMiddleware(Middleware):\n    def process_request(self, req):\n        print(\"Before dispatch\", req.url)\n\n    def process_response(self, req, res):\n        print(\"After dispatch\", req.url)\n\n\napp.add_middleware(SimpleCustomMiddleware)\n```\n\n### ORM\n\nAlcazar has a built-in ORM. Here is how you can use it:\n\n\n```python\n# connect to database\nfrom alcazar.orm import Database\n\ndb = Database(\"./test.db\")\n\n# define tables\nclass Author(Table):\n    name = Column(str)\n    age = Column(int)\n\nclass Book(Table):\n    title = Column(str)\n    published = Column(bool)\n    author = ForeignKey(Author)\n\n# create tables\ndb.create(Author)\ndb.create(Book)\n\n# create an instance and insert a row\ngreg = Author(name=\"George\", age=13)\ndb.save(greg)\n\n# fetch all rows\nauthors = db.all(Author)\n\n# get a specific row\nauthor = db.get(Author, 47)\n\n# save an object with a foreign key\nbook = Book(title=\"Building an ORM\", published=True, author=greg)\ndb.save(book)\n\n# fetch an object with a forein key\nprint(Book.get(55).author.name)\n\n# update an object\nbook.title = \"How to build an ORM\"\ndb.update(book)\n\n# delete an object\ndb.delete(Book, id=book.id)\n```\n\n## Features\n\n- WSGI compatible\n- Built-in ORM\n- Parameterized and basic routing\n- Class based handlers\n- Test Client\n- Support for templates\n- Support for static files\n- Custom exception handler\n- Middleware\n\n## Note\n\nIt is extremely raw and will hopefully keep improving. If you are interested in knowing how a particular feature is implemented in other\nframeworks, please open an issue and we will hopefully implement and explain it in a blog post.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frahmonov%2Falcazar","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frahmonov%2Falcazar","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frahmonov%2Falcazar/lists"}