pyframex7 0.1.0__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.
@@ -0,0 +1,101 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyframex7
3
+ Version: 0.1.0
4
+ Summary: This is simple python web framework which written for learning purposes
5
+ Home-page: https://github.com/Abduqodir7007/custom-framework
6
+ Author: Abduqodir Arifjanov
7
+ Author-email: abdukodirarifzanov@gmail.com
8
+ License: MIT
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.6
13
+ Classifier: Programming Language :: Python :: Implementation :: CPython
14
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
15
+ Requires-Python: >=3.6.0
16
+ Description-Content-Type: text/markdown
17
+ Requires-Dist: gunicorn==25.1.0
18
+ Requires-Dist: webob==1.8.9
19
+ Requires-Dist: requests==2.32.5
20
+ Requires-Dist: requests-wsgi-adapter==0.4.1
21
+ Dynamic: author
22
+ Dynamic: author-email
23
+ Dynamic: classifier
24
+ Dynamic: description
25
+ Dynamic: description-content-type
26
+ Dynamic: home-page
27
+ Dynamic: license
28
+ Dynamic: requires-dist
29
+ Dynamic: requires-python
30
+ Dynamic: summary
31
+
32
+
33
+
34
+ ![Purpose](https://img.shields.io/badge/purpose-learning-blue)
35
+ ![Status](https://img.shields.io/badge/status-experimental-orange)
36
+
37
+ > **PyFrame can be downloaded and installed from [PyPI](https://pypi.org/project/pyframe/).**
38
+
39
+ # PyFrame
40
+
41
+ PyFrame is a custom Python web framework created for learning purposes.
42
+
43
+ ## Features
44
+ - Minimal and easy to understand
45
+ - Custom middleware support
46
+ - Simple request/response handling
47
+ - Supports both class-based and function-based handlers
48
+ - Designed for educational use
49
+
50
+ ## Installation
51
+
52
+ You can install PyFrame locally by cloning this repository and installing it in editable mode:
53
+
54
+
55
+ PyFrame supports both function-based and class-based handlers for defining routes.
56
+
57
+ ### Function-Based Handler Example
58
+ ```python
59
+ from app import App
60
+ from response import Response
61
+
62
+ app = App()
63
+
64
+ @app.route("/")
65
+ def home(request):
66
+ return Response("Hello, World!")
67
+
68
+ ```
69
+
70
+ ### Class-Based Handler Example
71
+ ```python
72
+ from app import App, Handler
73
+ from response import Response
74
+
75
+ app = App()
76
+
77
+ class HelloHandler(Handler):
78
+ def get(self, request):
79
+ return Response("Hello from class-based handler!")
80
+
81
+ app.add_route("/hello", HelloHandler)
82
+ ```
83
+
84
+ - Define your routes using the `@app.route` decorator for function-based handlers or `app.add_route` for class-based handlers.
85
+ - Return a `Response` object from your route handlers.
86
+ - Run your application with `app.run()`.
87
+ app.run()
88
+ ```
89
+
90
+ - Define your routes using the `@app.route` decorator.
91
+ - Return a `Response` object from your route handlers.
92
+ - Run your application with `app.run()`.
93
+
94
+
95
+ ## Use Cases
96
+ - Learning how web frameworks work
97
+ - Experimenting with middleware and routing
98
+ - Building simple web applications for educational purposes
99
+
100
+ ## License
101
+ This project is for learning and educational purposes only.
@@ -0,0 +1,69 @@
1
+
2
+ ![Purpose](https://img.shields.io/badge/purpose-learning-blue)
3
+ ![Status](https://img.shields.io/badge/status-experimental-orange)
4
+
5
+ > **PyFrame can be downloaded and installed from [PyPI](https://pypi.org/project/pyframe/).**
6
+
7
+ # PyFrame
8
+
9
+ PyFrame is a custom Python web framework created for learning purposes.
10
+
11
+ ## Features
12
+ - Minimal and easy to understand
13
+ - Custom middleware support
14
+ - Simple request/response handling
15
+ - Supports both class-based and function-based handlers
16
+ - Designed for educational use
17
+
18
+ ## Installation
19
+
20
+ You can install PyFrame locally by cloning this repository and installing it in editable mode:
21
+
22
+
23
+ PyFrame supports both function-based and class-based handlers for defining routes.
24
+
25
+ ### Function-Based Handler Example
26
+ ```python
27
+ from app import App
28
+ from response import Response
29
+
30
+ app = App()
31
+
32
+ @app.route("/")
33
+ def home(request):
34
+ return Response("Hello, World!")
35
+
36
+ ```
37
+
38
+ ### Class-Based Handler Example
39
+ ```python
40
+ from app import App, Handler
41
+ from response import Response
42
+
43
+ app = App()
44
+
45
+ class HelloHandler(Handler):
46
+ def get(self, request):
47
+ return Response("Hello from class-based handler!")
48
+
49
+ app.add_route("/hello", HelloHandler)
50
+ ```
51
+
52
+ - Define your routes using the `@app.route` decorator for function-based handlers or `app.add_route` for class-based handlers.
53
+ - Return a `Response` object from your route handlers.
54
+ - Run your application with `app.run()`.
55
+ app.run()
56
+ ```
57
+
58
+ - Define your routes using the `@app.route` decorator.
59
+ - Return a `Response` object from your route handlers.
60
+ - Run your application with `app.run()`.
61
+
62
+
63
+ ## Use Cases
64
+ - Learning how web frameworks work
65
+ - Experimenting with middleware and routing
66
+ - Building simple web applications for educational purposes
67
+
68
+ ## License
69
+ This project is for learning and educational purposes only.
File without changes
@@ -0,0 +1,108 @@
1
+ import inspect
2
+ import requests
3
+ import wsgiadapter
4
+ from parse import parse
5
+ from webob import Request
6
+ from .response import Response
7
+ from .middleware import Middleware
8
+ from typing import List
9
+
10
+
11
+ class PyFramework:
12
+ def __init__(self) -> None:
13
+ self.routes = dict()
14
+ self.exception_handler = None
15
+ self.middleware = Middleware(self)
16
+
17
+ def __call__(self, environ, start_response):
18
+ # status = "200 CREATED"
19
+
20
+ # response_header = [("Content-type", "text/plain")]
21
+ # start_response(status, response_header)
22
+ return self.middleware(environ, start_response)
23
+
24
+ def wsgi_app(self, environ, start_response):
25
+ request = Request(environ)
26
+ response = self.handle_request(request)
27
+ return response(environ, start_response)
28
+
29
+ def add_exception_handler(self, handler):
30
+ self.exception_handler = handler
31
+
32
+ def handle_request(self, request):
33
+ response = Response()
34
+
35
+ handler, kwargs, allowed_methods = self.find_handler(request)
36
+
37
+ if inspect.isclass(handler):
38
+
39
+ handler_method = getattr(handler(), request.method.lower(), None)
40
+
41
+ if handler_method is None:
42
+ return self.method_not_allowed_response(request, response)
43
+ try:
44
+ handler_method(request, response, **kwargs)
45
+ except Exception as e:
46
+ if self.exception_handler is not None:
47
+ self.exception_handler(request, response, e)
48
+ else:
49
+ raise e
50
+ elif inspect.isfunction(handler):
51
+ try:
52
+ if request.method.lower() not in allowed_methods:
53
+ return self.method_not_allowed_response(request, response)
54
+
55
+ handler(request, response, **kwargs)
56
+
57
+ except Exception as e:
58
+ if self.exception_handler is not None:
59
+ self.exception_handler(request, response, e)
60
+ else:
61
+ raise e
62
+
63
+ else:
64
+ self.default_response(response)
65
+
66
+ return response
67
+
68
+ def find_handler(self, request):
69
+ allowed_method = None
70
+ for path, handler in self.routes.items():
71
+ handler, allowed_method = handler
72
+
73
+ result = parse(path, request.path)
74
+
75
+ if result is not None:
76
+ return handler, result.named, allowed_method
77
+
78
+ return None, None, allowed_method
79
+
80
+ def add_middleware(self, middleware_class):
81
+ self.middleware.add(middleware_class)
82
+
83
+ def default_response(self, response):
84
+ response.status_code = 404
85
+ response.text = "Not found"
86
+
87
+ def method_not_allowed_response(self, request, response):
88
+ response.text = "METHOD NOT ALLOWED"
89
+ response.status_code = 405
90
+ return response
91
+
92
+ def router(self, path: str, allowed_methods: List[str] = ["get"]):
93
+ assert path not in self.routes, f"Path {path} already exists"
94
+
95
+ def wrapper(handler):
96
+ self.routes[path] = (handler, allowed_methods)
97
+ return handler
98
+
99
+ return wrapper
100
+
101
+ def add_router(self, path, handler, allowed_methods: List[str] = ["get"]):
102
+ assert path not in self.routes, f"Path {path} already exists"
103
+ self.routes[path] = (handler, allowed_methods)
104
+
105
+ def test_session(self):
106
+ session = requests.Session()
107
+ session.mount("http://testserver", wsgiadapter.WSGIAdapter(self))
108
+ return session
@@ -0,0 +1,30 @@
1
+ from webob import Request, Response
2
+ from typing import Any
3
+
4
+
5
+ class Middleware:
6
+ def __init__(self, app):
7
+ self.app = app
8
+
9
+ def add(self, middleware_class):
10
+ self.app = middleware_class(self.app)
11
+
12
+ def process_request(self, request):
13
+ pass
14
+
15
+ def process_response(self, request, response):
16
+ pass
17
+
18
+ def handle_request(self, request):
19
+ self.process_request(request)
20
+ response = self.app.handle_request(request)
21
+ self.process_response(request, response)
22
+
23
+ return response
24
+
25
+ def __call__(self, environ, start_response):
26
+ request = Request(environ)
27
+ response = self.app.handle_request(request)
28
+
29
+ return response(environ, start_response)
30
+
@@ -0,0 +1,31 @@
1
+ import json
2
+ from typing import Any
3
+
4
+ from webob import Response as WebobResponse
5
+
6
+
7
+ class Response:
8
+ def __init__(self) -> None:
9
+ self.json = None
10
+ self.text = None
11
+ self.content_type = None
12
+ self.body = b""
13
+ self.status_code = 200
14
+
15
+ def change_response(self):
16
+
17
+ if self.json is not None:
18
+ self.body = json.dumps(self.json).encode()
19
+ self.content_type = "application/json"
20
+
21
+ if self.text is not None:
22
+ self.body = self.text
23
+ self.content_type = "text/plain"
24
+
25
+ def __call__(self, environ, start_response) -> Any:
26
+ self.change_response()
27
+ response = WebobResponse(
28
+ body=self.body, status=self.status_code, content_type=self.content_type
29
+ )
30
+
31
+ return response(environ, start_response)
@@ -0,0 +1,101 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyframex7
3
+ Version: 0.1.0
4
+ Summary: This is simple python web framework which written for learning purposes
5
+ Home-page: https://github.com/Abduqodir7007/custom-framework
6
+ Author: Abduqodir Arifjanov
7
+ Author-email: abdukodirarifzanov@gmail.com
8
+ License: MIT
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.6
13
+ Classifier: Programming Language :: Python :: Implementation :: CPython
14
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
15
+ Requires-Python: >=3.6.0
16
+ Description-Content-Type: text/markdown
17
+ Requires-Dist: gunicorn==25.1.0
18
+ Requires-Dist: webob==1.8.9
19
+ Requires-Dist: requests==2.32.5
20
+ Requires-Dist: requests-wsgi-adapter==0.4.1
21
+ Dynamic: author
22
+ Dynamic: author-email
23
+ Dynamic: classifier
24
+ Dynamic: description
25
+ Dynamic: description-content-type
26
+ Dynamic: home-page
27
+ Dynamic: license
28
+ Dynamic: requires-dist
29
+ Dynamic: requires-python
30
+ Dynamic: summary
31
+
32
+
33
+
34
+ ![Purpose](https://img.shields.io/badge/purpose-learning-blue)
35
+ ![Status](https://img.shields.io/badge/status-experimental-orange)
36
+
37
+ > **PyFrame can be downloaded and installed from [PyPI](https://pypi.org/project/pyframe/).**
38
+
39
+ # PyFrame
40
+
41
+ PyFrame is a custom Python web framework created for learning purposes.
42
+
43
+ ## Features
44
+ - Minimal and easy to understand
45
+ - Custom middleware support
46
+ - Simple request/response handling
47
+ - Supports both class-based and function-based handlers
48
+ - Designed for educational use
49
+
50
+ ## Installation
51
+
52
+ You can install PyFrame locally by cloning this repository and installing it in editable mode:
53
+
54
+
55
+ PyFrame supports both function-based and class-based handlers for defining routes.
56
+
57
+ ### Function-Based Handler Example
58
+ ```python
59
+ from app import App
60
+ from response import Response
61
+
62
+ app = App()
63
+
64
+ @app.route("/")
65
+ def home(request):
66
+ return Response("Hello, World!")
67
+
68
+ ```
69
+
70
+ ### Class-Based Handler Example
71
+ ```python
72
+ from app import App, Handler
73
+ from response import Response
74
+
75
+ app = App()
76
+
77
+ class HelloHandler(Handler):
78
+ def get(self, request):
79
+ return Response("Hello from class-based handler!")
80
+
81
+ app.add_route("/hello", HelloHandler)
82
+ ```
83
+
84
+ - Define your routes using the `@app.route` decorator for function-based handlers or `app.add_route` for class-based handlers.
85
+ - Return a `Response` object from your route handlers.
86
+ - Run your application with `app.run()`.
87
+ app.run()
88
+ ```
89
+
90
+ - Define your routes using the `@app.route` decorator.
91
+ - Return a `Response` object from your route handlers.
92
+ - Run your application with `app.run()`.
93
+
94
+
95
+ ## Use Cases
96
+ - Learning how web frameworks work
97
+ - Experimenting with middleware and routing
98
+ - Building simple web applications for educational purposes
99
+
100
+ ## License
101
+ This project is for learning and educational purposes only.
@@ -0,0 +1,12 @@
1
+ README.md
2
+ setup.py
3
+ pyframe/__init__.py
4
+ pyframe/app.py
5
+ pyframe/middleware.py
6
+ pyframe/response.py
7
+ pyframex7.egg-info/PKG-INFO
8
+ pyframex7.egg-info/SOURCES.txt
9
+ pyframex7.egg-info/dependency_links.txt
10
+ pyframex7.egg-info/requires.txt
11
+ pyframex7.egg-info/top_level.txt
12
+ tests/test_app.py
@@ -0,0 +1,4 @@
1
+ gunicorn==25.1.0
2
+ webob==1.8.9
3
+ requests==2.32.5
4
+ requests-wsgi-adapter==0.4.1
@@ -0,0 +1 @@
1
+ pyframe
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,134 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ # Note: To use the 'upload' functionality of this file, you must:
5
+ # $ pipenv install twine --dev
6
+
7
+ import io
8
+ import os
9
+ import sys
10
+ from shutil import rmtree
11
+
12
+ from setuptools import find_packages, setup, Command
13
+
14
+ # Package meta-data.
15
+ NAME = 'pyframex7'
16
+ DESCRIPTION = 'This is simple python web framework which written for learning purposes'
17
+ URL = 'https://github.com/Abduqodir7007/custom-framework'
18
+ EMAIL = 'abdukodirarifzanov@gmail.com'
19
+ AUTHOR = 'Abduqodir Arifjanov'
20
+ REQUIRES_PYTHON = '>=3.6.0'
21
+ VERSION = '0.1.0'
22
+
23
+ # What packages are required for this module to be executed?
24
+ REQUIRED = [
25
+ 'gunicorn==25.1.0',
26
+ 'webob==1.8.9',
27
+ 'requests==2.32.5',
28
+ 'requests-wsgi-adapter==0.4.1',
29
+ ]
30
+
31
+ # What packages are optional?
32
+ EXTRAS = {
33
+ # 'fancy feature': ['django'],
34
+ }
35
+
36
+ # The rest you shouldn't have to touch too much :)
37
+ # ------------------------------------------------
38
+ # Except, perhaps the License and Trove Classifiers!
39
+ # If you do change the License, remember to change the Trove Classifier for that!
40
+
41
+ here = os.path.abspath(os.path.dirname(__file__))
42
+
43
+ # Import the README and use it as the long-description.
44
+ # Note: this will only work if 'README.md' is present in your MANIFEST.in file!
45
+ try:
46
+ with io.open(os.path.join(here, 'README.md'), encoding='utf-8') as f:
47
+ long_description = '\n' + f.read()
48
+ except FileNotFoundError:
49
+ long_description = DESCRIPTION
50
+
51
+ # Load the package's __version__.py module as a dictionary.
52
+ about = {}
53
+ if not VERSION:
54
+ project_slug = NAME.lower().replace("-", "_").replace(" ", "_")
55
+ with open(os.path.join(here, project_slug, '__version__.py')) as f:
56
+ exec(f.read(), about)
57
+ else:
58
+ about['__version__'] = VERSION
59
+
60
+
61
+ class UploadCommand(Command):
62
+ """Support setup.py upload."""
63
+
64
+ description = 'Build and publish the package.'
65
+ user_options = []
66
+
67
+ @staticmethod
68
+ def status(s):
69
+ """Prints things in bold."""
70
+ print('\033[1m{0}\033[0m'.format(s))
71
+
72
+ def initialize_options(self):
73
+ pass
74
+
75
+ def finalize_options(self):
76
+ pass
77
+
78
+ def run(self):
79
+ try:
80
+ self.status('Removing previous builds…')
81
+ rmtree(os.path.join(here, 'dist'))
82
+ except OSError:
83
+ pass
84
+
85
+ self.status('Building Source and Wheel (universal) distribution…')
86
+ os.system('{0} setup.py sdist bdist_wheel --universal'.format(sys.executable))
87
+
88
+ self.status('Uploading the package to PyPI via Twine…')
89
+ os.system('twine upload dist/*')
90
+
91
+ self.status('Pushing git tags…')
92
+ os.system('git tag v{0}'.format(about['__version__']))
93
+ os.system('git push --tags')
94
+
95
+ sys.exit()
96
+
97
+
98
+ # Where the magic happens:
99
+ setup(
100
+ name=NAME,
101
+ version=about['__version__'],
102
+ description=DESCRIPTION,
103
+ long_description=long_description,
104
+ long_description_content_type='text/markdown',
105
+ author=AUTHOR,
106
+ author_email=EMAIL,
107
+ python_requires=REQUIRES_PYTHON,
108
+ url=URL,
109
+ packages=find_packages(exclude=["tests", "*.tests", "*.tests.*", "tests.*"]),
110
+ # If your package is a single module, use this instead of 'packages':
111
+ # py_modules=['mypackage'],
112
+
113
+ # entry_points={
114
+ # 'console_scripts': ['mycli=mymodule:cli'],
115
+ # },
116
+ install_requires=REQUIRED,
117
+ extras_require=EXTRAS,
118
+ include_package_data=True,
119
+ license='MIT',
120
+ classifiers=[
121
+ # Trove classifiers
122
+ # Full list: https://pypi.python.org/pypi?%3Aaction=list_classifiers
123
+ 'License :: OSI Approved :: MIT License',
124
+ 'Programming Language :: Python',
125
+ 'Programming Language :: Python :: 3',
126
+ 'Programming Language :: Python :: 3.6',
127
+ 'Programming Language :: Python :: Implementation :: CPython',
128
+ 'Programming Language :: Python :: Implementation :: PyPy'
129
+ ],
130
+ # $ setup.py publish support.
131
+ cmdclass={
132
+ 'upload': UploadCommand,
133
+ },
134
+ )
@@ -0,0 +1,182 @@
1
+ import pytest
2
+ import json
3
+ from pyframe.middleware import Middleware
4
+ from tests.conftest import app, test_client
5
+ from pyframe.app import PyFramework
6
+
7
+
8
+ class TestApp:
9
+ def test_route_registration(self, app: PyFramework):
10
+
11
+ @app.router("/home/")
12
+ def home(request, response):
13
+ response.text = "Hello from home"
14
+
15
+ def test_duplicate_route_registration(self, app: PyFramework):
16
+
17
+ @app.router("/home/")
18
+ def home(request, response):
19
+ response.text = "Hello from home"
20
+
21
+ with pytest.raises(Exception):
22
+
23
+ @app.router("/home/")
24
+ def home2(request, response):
25
+ response.text = "Hello from home"
26
+
27
+ def test_client(self, app: PyFramework, test_client):
28
+
29
+ @app.router("/home/")
30
+ def home(request, response):
31
+ response.text = "Hello from home"
32
+
33
+ res = test_client.get("http://testserver/home/")
34
+
35
+ assert res.text == "Hello from home"
36
+
37
+ def test_parametized_routing(self, app: PyFramework, test_client):
38
+
39
+ @app.router("/home/{name}/")
40
+ def home(request, response, name):
41
+ response.text = f"Hello {name}"
42
+
43
+ res = test_client.get("http://testserver/home/john/")
44
+
45
+ assert res.text == "Hello john"
46
+
47
+ def test_wrong_routes(self, test_client):
48
+
49
+ res = test_client.get("http://testserver/home/")
50
+
51
+ assert res.text == "Not found"
52
+ assert res.status_code == 404
53
+
54
+ def test_class_based_get_routing(self, app, test_client):
55
+
56
+ @app.router("/book/")
57
+ class Book:
58
+
59
+ def get(self, request, response):
60
+ response.text = "Getting books"
61
+
62
+ res1 = test_client.get("http://testserver/book/")
63
+ res2 = test_client.post("http://testserver/book/")
64
+
65
+ assert res1.text == "Getting books"
66
+ assert res2.text == "METHOD NOT ALLOWED"
67
+ assert res2.status_code == 405
68
+
69
+ def test_django_like_routing(self, app, test_client):
70
+
71
+ def book(request, response):
72
+ response.text = "Hello from book"
73
+
74
+ app.add_router("/book/", book)
75
+ res = test_client.get("http://testserver/book/")
76
+
77
+ assert res.text == "Hello from book"
78
+
79
+ def test_custom_exception_handler(self, app: PyFramework, test_client):
80
+
81
+ def on_exception(request, response, exp_class):
82
+ response.text = "Something went wrong"
83
+
84
+ app.add_exception_handler(on_exception)
85
+
86
+ @app.router("/exception/")
87
+ def exception(request, response):
88
+ raise AttributeError("some exception")
89
+
90
+ res = test_client.get("http://testserver/exception/")
91
+
92
+ assert res.text == "Something went wrong"
93
+
94
+ def test_custom_exception_handler_witj_class_based_router(
95
+ self, app: PyFramework, test_client
96
+ ):
97
+
98
+ def on_exception(request, response, exception):
99
+ response.text = "Exception from class"
100
+
101
+ app.add_exception_handler(on_exception)
102
+
103
+ @app.router("/book/")
104
+ class Book:
105
+
106
+ def get(self, request, response):
107
+ raise AttributeError("Error")
108
+
109
+ res = test_client.get("http://testserver/book/")
110
+
111
+ assert res.text == "Exception from class"
112
+
113
+ def test_middleware_methods_are_called(self, app: PyFramework, test_client):
114
+ is_process_request_called = False
115
+ is_process_response_called = False
116
+
117
+ class SimpleMiddleware(Middleware):
118
+ def __init__(self, app):
119
+ super().__init__(app)
120
+
121
+ def process_request(self, request):
122
+
123
+ nonlocal is_process_request_called
124
+ is_process_request_called = True
125
+
126
+ def process_response(self, request, response):
127
+ nonlocal is_process_response_called
128
+ is_process_response_called = True
129
+
130
+ app.add_middleware(SimpleMiddleware)
131
+
132
+ @app.router("/home/")
133
+ def home(request, response):
134
+ response.text = "Hello from middleware"
135
+
136
+ res = test_client.get("http://testserver/home/")
137
+
138
+ assert is_process_response_called is True
139
+ assert is_process_request_called is True
140
+
141
+ def test_function_based_allowed_method(self, app: PyFramework, test_client):
142
+
143
+ @app.router("/book/", allowed_methods=["get"])
144
+ def book(request, response):
145
+ response.text = "Hello world"
146
+
147
+ res = test_client.get("http://testserver/book/")
148
+
149
+ assert res.text == "Hello world"
150
+
151
+ def test_function_based_allowed_method_with_wrong_method(self, app, test_client):
152
+
153
+ @app.router("/book/", allowed_methods=["post"])
154
+ def book(request, response):
155
+ response.text = "Checking method allowed"
156
+
157
+ res = test_client.get("http://testserver/book/")
158
+
159
+ assert res.text == "METHOD NOT ALLOWED"
160
+
161
+ def test_json_response(self, app, test_client):
162
+
163
+ @app.router("/json")
164
+ def json_handler(request, response):
165
+
166
+ response_data = {"name": "tom", "number": 10}
167
+
168
+ response.body = response_data
169
+ res = test_client.get("http://testserver/json/").json()
170
+
171
+ assert res.headers["Content-Type"] == "application/json"
172
+ assert res.name == "tom"
173
+
174
+ def test_text_response(self, app, test_client):
175
+
176
+ @app.router("/text/")
177
+ def text_handler(request, response):
178
+ response.text = "this is the response"
179
+
180
+ res = test_client.get("http://testserver/text/")
181
+
182
+ assert "text/plain" in res.headers["Content-Type"]