diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4b387ba..a5a4f47 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,7 +32,7 @@ jobs: fetch-depth: 0 - name: Build wheels - uses: pypa/cibuildwheel@v2.15.0 + uses: pypa/cibuildwheel@v2.16.5 env: CIBW_ARCHS_MACOS: x86_64 HATCH_BUILD_HOOKS_ENABLE: "true" diff --git a/CHANGELOG.md b/CHANGELOG.md index 5162101..32faacc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,106 +5,109 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased - -- Fixed `template` attribute with the `view` template renderer -- Added the `context` decorator and the `Context` type -- Added the `headers` parameter to functions on `TestingContext` -- Modified some behavior of automatic route inputs -- Fixed syntax errors in `view init` -- Added `Route.middleware` -- Routes with equivalent paths but different methods now return `405 Method Not Allowed` when accessed -- Added `route` and `App.route` -- Added docstrings to router functions -- Added the `JSON` response class -- Added the `custom` body translate strategy -- Made `method` a keyword-only parameter in `path` -- Added the `extract_path` utility -- Added the `view build` command -- Added `App.template` -- Route errors now display the error message when `dev` is `True` -- Changed exception rendering in route errors to use the `rich` renderer -- Added `compile_type` and `TCValidator` -- Added `markdown` and `App.markdown` -- Added the `Error` class -- Added the `error_class` parameter to both `new_app` and `App` -- Added the `ERROR_CODES` constant -- **Breaking Change:** The `body` parameter in `Response` is now required +## [1.0.0-alpha9] - 2024-2-4 + +- Fixed `template` attribute with the `view` template renderer +- Added the `context` decorator and the `Context` type +- Added the `headers` parameter to functions on `TestingContext` +- Modified some behavior of automatic route inputs +- Fixed syntax errors in `view init` +- Added `Route.middleware` +- Routes with equivalent paths but different methods now return `405 Method Not Allowed` when accessed +- Added `route` and `App.route` +- Added docstrings to router functions +- Added the `JSON` response class +- Added the `custom` body translate strategy +- Made `method` a keyword-only parameter in `path` +- Added the `extract_path` utility +- Added the `view build` command +- Added `App.template` +- Route errors now display the error message when `dev` is `True` +- Changed exception rendering in route errors to use the `rich` renderer +- Added `compile_type` and `TCValidator` +- Added `markdown` and `App.markdown` +- Added the `Error` class +- Added the `error_class` parameter to both `new_app` and `App` +- Added the `ERROR_CODES` constant +- Completely rewrote docs +- **Breaking Change:** The `body` parameter in `Response` is now required ## [1.0.0-alpha8] - 2024-1-21 -- Added optional dependencies for `databases` and `templates` -- Added environment prefixes for database configuration -- Added `templates` and `TemplatesConfig` to config -- Added the `templates` function -- Added support for `attrs` in type validation -- Added documentation for caching -- Added the `cache_rate` parameter to routers -- Removed `psutil` and `plotext` as a global dependency -- Added `fancy` optional dependencies -- Fixed route inputs with synchronous routes -- **Breaking Change:** Route inputs are now applied in the order of the decorator call as it appears in code +- Added optional dependencies for `databases` and `templates` +- Added environment prefixes for database configuration +- Added `templates` and `TemplatesConfig` to config +- Added the `templates` function +- Added support for `attrs` in type validation +- Added documentation for caching +- Added the `cache_rate` parameter to routers +- Removed `psutil` and `plotext` as a global dependency +- Added `fancy` optional dependencies +- Fixed route inputs with synchronous routes +- **Breaking Change:** Route inputs are now applied in the order of the decorator call as it appears in code ## [1.0.0-alpha7] - 2023-12-7 **Quick Patch Release** -- Remerged new `view init` command. +- Remerged new `view init` command. ## [1.0.0-alpha6] - 2023-11-30 -- Added `get_app` -- Added documentation generation -- Added database support (NOT FINISHED) -- Removed `attempt_import` and `MissingLibraryError` -- Added support for lists in type validation -- Added support for implicit query parameters -- Renamed `debug` to `enable_debug` -- Added `debug`, `info`, `warning`, `error`, and `critical` logging functions -- Added `InvalidRouteError`, `DuplicateRouteError`, `ViewInternalError`, and `ConfigurationError` -- Renamed `EnvironmentError` to `BadEnvironmentError` -- Added logging functions to `App` -- Changed environment prefixes for configuration -- Rewrote documentation -- Added `patterns` loader -- Added handling of relative paths in the configuration setting `loader_path` -- Added exists validation to `loader_path` -- Add path to `PATH` environment variable during loading -- Upgraded `view init` +- Added `get_app` +- Added documentation generation +- Added database support (NOT FINISHED) +- Removed `attempt_import` and `MissingLibraryError` +- Added support for lists in type validation +- Added support for implicit query parameters +- Renamed `debug` to `enable_debug` +- Added `debug`, `info`, `warning`, `error`, and `critical` logging functions +- Added `InvalidRouteError`, `DuplicateRouteError`, `ViewInternalError`, and `ConfigurationError` +- Renamed `EnvironmentError` to `BadEnvironmentError` +- Added logging functions to `App` +- Changed environment prefixes for configuration +- Rewrote documentation +- Added `patterns` loader +- Added handling of relative paths in the configuration setting `loader_path` +- Added exists validation to `loader_path` +- Add path to `PATH` environment variable during loading +- Upgraded `view init` ## [1.0.0-alpha5] - 2023-09-24 -- Added `app.query` and `app.body` -- Patched warning with starting app from incorrect filename -- Updated `__all__` for `routing.py` -- Added `view.Response` and `view.HTML` -- Fixed `__view_result__` -- Added support for `__view_body__` and `__view_construct__` -- Added support for Pydantic, `NamedTuple`, and dataclasses for type validation -- Support for direct union types (i.e. `str | int`, `Union[str, int]`) on type validation -- Added support for non async routes +- Added `app.query` and `app.body` +- Patched warning with starting app from incorrect filename +- Updated `__all__` for `routing.py` +- Added `view.Response` and `view.HTML` +- Fixed `__view_result__` +- Added support for `__view_body__` and `__view_construct__` +- Added support for Pydantic, `NamedTuple`, and dataclasses for type validation +- Support for direct union types (i.e. `str | int`, `Union[str, int]`) on type validation +- Added support for non async routes ## [1.0.0-alpha4] - 2023-09-10 -- Added type validation (without support for `__view_body__`) -- Patched query strings on app testing -- Added tests for query and body parameters -- Patched body parameters -- Documented type validation -- Patched bodies with testing + +- Added type validation (without support for `__view_body__`) +- Patched query strings on app testing +- Added tests for query and body parameters +- Patched body parameters +- Documented type validation +- Patched bodies with testing ## [1.0.0-alpha3] - 2023-09-9 -- Patched header responses -- Added tests for headers -- Updated repr for `Route` -- Patched responses with three values -- Documented responses and result protocol + +- Patched header responses +- Added tests for headers +- Updated repr for `Route` +- Patched responses with three values +- Documented responses and result protocol ## [1.0.0-alpha2] - 2023-09-9 -- Added `App.test()` -- Added warning when filename does not match `app_path` -- Added more tests -- Upgrade CIBW to work on 3.11 +- Added `App.test()` +- Added warning when filename does not match `app_path` +- Added more tests +- Upgrade CIBW to work on 3.11 ## [1.0.0-alpha1] - 2023-08-17 diff --git a/docs/building-projects/app_basics.md b/docs/building-projects/app_basics.md index 311e0f7..715c55c 100644 --- a/docs/building-projects/app_basics.md +++ b/docs/building-projects/app_basics.md @@ -8,26 +8,27 @@ Every view project will have a `new_app` call. The simplest app looks like this: from view import new_app app = new_app() +app.run() # you'll learn about this later ``` `new_app` does a few important things: -- Loads the configuration, regardless of whether a config file exists. -- Sets the `App` address for use by `get_app` (more on that later). -- Loads finalization code for when the app closes. +- Loads the configuration, regardless of whether a config file exists. +- Sets the `App` address for use by `get_app` (more on that later). +- Loads finalization code for when the app closes. While it's not required for every app, naming your app variable `app` is the proper convention for view, as that's the default variable searched for when using the `view serve` command, but more on that in a moment. For now, just try to stick with naming your app file `app.py` and your `view.App` instance `app`. -::: view.app.App +::: view.app.new_app ## Launching Apps Python libraries generally have two ways to run a web server: -- Running via the command line. -- Launching from Python itself (e.g. a `server.start(...)` function). +- Running via the command line. +- Launching from Python itself (e.g. a `server.start(...)` function). Both have their benefits and downsides, so view.py supports both out of the box. `App` comes with its `run()` method, and the view CLI has the `view serve` command. @@ -54,17 +55,17 @@ app.run() print("you called the app with view serve") # this only runs when `view serve` is used ``` -::: view.app.App.run - ### Fancy Mode View comes with something called "fancy mode", which is a fancy UI that shows when you run the app. If you would like to disable this, you can do one of two things: -- Disable the `fancy` setting in configuration. -- Pass `fancy=False` to `run()`. +- Disable the `fancy` setting in configuration. +- Pass `fancy=False` to `run()`. You should disable it in the configuration if you completely despise fancy mode and don't want to use it at all, but if you only want to temporarily turn it off (for example, if you're a view.py developer and need to see proper output) then pass `fancy=False`. +::: view.app.App.run + ## Getting the App ### Circular Imports @@ -117,6 +118,6 @@ def index(): Every view.py project should contain a call to `new_app`. `new_app` does important things like loading your configuration, set's up finalization code, and letting the `App` instance be used by `get_app`. -Running an app can be done in two ways: programmatically via the `App.run` or through `view serve` command. However, every view.py app should contain an `App.run` to give the choice for running programmatically. By default, view.py has a fancy UI when running your app, which may be disabled via editing the config or passing `fancy=False` to `run()`. +Running an app can be done in two ways: programmatically via the `App.run` or through `view serve` command. However, every view.py app should contain an `App.run` to give the choice for running programmatically. By default, view.py has a fancy UI when running your app, which may be disabled via editing the config or passing `fancy=False` to `run()`. Finally, circular imports occur when two Python modules try to import each other, which can happen a lot in view when getting the app from the app file (especially in manual routing). To fix it, View provides a `get_app` function to get you your `App` instance without an import. diff --git a/docs/building-projects/parameters.md b/docs/building-projects/parameters.md index 9036963..f878542 100644 --- a/docs/building-projects/parameters.md +++ b/docs/building-projects/parameters.md @@ -8,10 +8,10 @@ In HTTP, the query string is at the end of the URL and is prefixed with a `?`. F Bodies are a little bit more complicated. An HTTP body can be a number of different formats, but in a nutshell they are again, key-value pairs, but they can be a number of types. For now, JSON will be the main focus, which can have `str` keys, and any of the following as a value (in terms of Python types): -- `str` -- `int` -- `bool` -- `dict[str, ]` +- `str` +- `int` +- `bool` +- `dict[str, ]` The main similiarity here is that they are both key value pairs, which will make more sense in a moment. @@ -19,8 +19,8 @@ The main similiarity here is that they are both key value pairs, which will make In view.py, a route input is anything that feeds a parameter (or "input") to the route. This can be either a parameter received through the HTTP body, or something taken from the query string. View treats these two essentially the same on the user's end. Route inputs are similar to routes in the sense that there are standard and direct versions of the same thing: -- `query` or `App.query` -- `body` or `App.body` +- `query` or `App.query` +- `body` or `App.body` There is little to no difference between the standard and direct variations, **including loading**. The direct versions are only to be used when the app is already available to **prevent extra imports**. @@ -31,8 +31,8 @@ There is little to no difference between the standard and direct variations, **i For documentation purposes, only `query` variations will be used. However, **`body` works the exact same way**. A route input function (`query` in this case) takes one or more parameters: -- The name of the parameter, should be a `str`. -- The type that it expects (optional). Note that this can be passed as many times as you want, and each type is just treated as a union. +- The name of the parameter, should be a `str`. +- The type that it expects (optional). Note that this can be passed as many times as you want, and each type is just treated as a union. The below code would expect a parameter in the query string named `hello` of type `int`: @@ -65,7 +65,7 @@ async def index(hello: int): from view import new_app app = new_app() - + @app.get("/") @app.query("hello", str) @app.query("world", str) @@ -92,7 +92,7 @@ app.run() Note that automatic inputs create inputs for **query parameters only**. !!! note - + When mixing automatic route inputs with decorators (e.g. `query` and `body`), view.py assumes that decorator inputs have the same name as the parameter. For example, the following will not work: ```py @@ -114,10 +114,10 @@ In query strings, only a string can be sent, but these strings can represent oth View has this exact same behavior when it comes to route inputs. If you tell your route to take an `int`, view.py will do all the necessary computing internally to make sure that you get an integer in your route. If a proper integer was not sent, then the server will automatically return an error `400` (Bad Request). There are a few things that should be noted for this behavior: -- Every type can be casted to `str`. -- Every type can be casted to `Any`. -- `bool` expects `true` and `false` (instead of Python's `True` and `False`) to fit with JSON's types. -- `dict` expects valid JSON, **not** a valid Python dictionary. +- Every type can be casted to `str`. +- Every type can be casted to `Any`. +- `bool` expects `true` and `false` (instead of Python's `True` and `False`) to fit with JSON's types. +- `dict` expects valid JSON, **not** a valid Python dictionary. ## Typing Inputs @@ -149,19 +149,19 @@ app.run() The types supported are (all of which can be mixed and matched to your needs): -- `str` -- `int` -- `bool` -- `list` (or `typing.List`) -- `dict` (or `typing.Dict`) -- `Any` (as in `typing.Any`) -- `None` -- `dataclasses.dataclass` -- `pydantic.BaseModel` -- Classes decorated with `attrs.define` -- `typing.NamedTuple` -- `typing.TypedDict` -- Any object supporting the `__view_body__` protocol. +- `str` +- `int` +- `bool` +- `list` (or `typing.List`) +- `dict` (or `typing.Dict`) +- `Any` (as in `typing.Any`) +- `None` +- `dataclasses.dataclass` +- `pydantic.BaseModel` +- Classes decorated with `attrs.define` +- `typing.NamedTuple` +- `typing.TypedDict` +- Any object supporting the `__view_body__` protocol. ### Lists and Dictionaries @@ -192,9 +192,9 @@ Note that backport is **not possible** if you're using new typing features (such As listed about earlier, view.py supports a few different objects to be used as types. All of these objects are meant for holding data to a specific model, which can be incredibly useful in developing web apps. Some things should be noted when using these types: -- Any annotated value types must an available type already (i.e. `str | int` is supported, but `set | str` is not). Other objects are indeed supported. -- Respected modifiers are supported (such as `dataclasses.field` on `dataclass`). -- Methods are unrelated to the parsing, and may return any type and take any parameters. Methods are not accessible to the user (as JSON doesn't have methods). +- Any annotated value types must an available type already (i.e. `str | int` is supported, but `set | str` is not). Other objects are indeed supported. +- Respected modifiers are supported (such as `dataclasses.field` on `dataclass`). +- Methods are unrelated to the parsing, and may return any type and take any parameters. Methods are not accessible to the user (as JSON doesn't have methods). Here's an example using `dataclasses`: @@ -254,9 +254,9 @@ validator = compile_type(str | int) With a validator, you can do three things: -- Cast an object to the type. -- Check if an object is compatible with the type. -- Check if an object is compatible, without the use of casting. +- Cast an object to the type. +- Check if an object is compatible with the type. +- Check if an object is compatible, without the use of casting. `cast` will raise a `TypeValidationError` if the type is not compatible: @@ -270,7 +270,7 @@ tp.cast("123") # TypeValidationError The difference between `check_type` and `is_compatible`, is that `check_type` is a [type guard](https://mypy.readthedocs.io/en/latest/type_narrowing.html), which `is_compatible` is not. -This means that `check_type` will ensure that the object is *an instance* of the type, while `is_compatible` checks whether it can be casted. For example: +This means that `check_type` will ensure that the object is _an instance_ of the type, while `is_compatible` checks whether it can be casted. For example: ```py from view import compile_type @@ -293,9 +293,9 @@ if tp.is_compatible(y): If any of the above types do not support your needs, you may design your own type with the `__view_body__` protocol. On a type, `__view_body__` can be held in one of two things: -- An attribute (e.g. `cls.__view_body__ = ...`) -- A property -- A static (or class) method. +- An attribute (e.g. `cls.__view_body__ = ...`) +- A property +- A static (or class) method. Whichever way you choose, the `__view_body__` data must be accessed statically, **not in an instance**. The data should be a dictionary (containing only `str` keys, once again), but the values should be types, not instances. These types outline how view.py should parse it at runtime. For example, a `__view_body__` to create an object that has a key called `a`, which a `str` value would look like so: @@ -312,6 +312,7 @@ class MyObject: @classmethod def __view_construct__(cls, **kwargs): + self = cls() self.a: str = kwargs["a"] ``` @@ -328,6 +329,7 @@ class MyObject: @classmethod def __view_construct__(cls, **kwargs): + self = cls() self.a: str | int = kwargs["a"] self.a: str = kwargs["b"] ``` diff --git a/docs/building-projects/responses.md b/docs/building-projects/responses.md index bcb3361..597e275 100644 --- a/docs/building-projects/responses.md +++ b/docs/building-projects/responses.md @@ -35,8 +35,8 @@ app.run() However, manually returning can be messy. For this, view.py provides you the `Error` class, which behaves like an `Exception`. It takes two parameters: -- The status code, which is `400` by default. -- The message to send back to the user. If this is `None`, it uses the default error message (e.g. `Bad Request` for error `400`). +- The status code, which is `400` by default. +- The message to send back to the user. If this is `None`, it uses the default error message (e.g. `Bad Request` for error `400`). Since `Error` works like a Python exception, you can `raise` it just fine: @@ -59,6 +59,8 @@ app.run() `Error` can only be used to send back *error* responses. It can **not** be used to return status codes such as `200`. +::: view.app.Error + ## Caching Sometimes, computing the response for a route can be expensive or unnecessary. For this, view.py, along with many other web frameworks, provide the ability to cache responses. @@ -100,12 +102,12 @@ In the above example, `index` is only called every 10 requests, so after 20 call ## Response Protocol -If you have some sort of object that you want to wrap a response around, view.py gives you the `__view_response__` protocol. The only requirements are: +If you have some sort of object that you want to wrap a response around, view.py gives you the `__view_result__` protocol. The only requirements are: -- `__view_response__` is available on the returned object (doesn't matter if it's static or instance) -- `__view_response__` returns data that corresponds to the allowed return values. +- `__view_result__` is available on the returned object (doesn't matter if it's static or instance) +- `__view_result__` returns data that corresponds to the allowed return values. -For example, a type `MyObject` defining `__view_response__` could look like: +For example, a type `MyObject` defining `__view_result__` could look like: ```py from view import new_app @@ -113,7 +115,7 @@ from view import new_app app = new_app() class MyObject: - def __view_response__(self): + def __view_result__(self): return "Hello from MyObject!", {"x-www-myobject": "foo"} @app.get("/") @@ -138,15 +140,15 @@ async def index(): View comes with two built in response objects: `Response` and `HTML`. -- `Response` is simply a wrapper around other responses. -- `HTML` is for returning HTML content. -- `JSON` is for returning JSON content. +- `Response` is simply a wrapper around other responses. +- `HTML` is for returning HTML content. +- `JSON` is for returning JSON content. ::: view.response.Response ::: view.response.HTML ::: view.response.JSON -A common use case for `Response` is wrapping an object that has a `__view_response__` and changing one of the values. For example: +A common use case for `Response` is wrapping an object that has a `__view_result__` and changing one of the values. For example: ```py from view import new_app, Response @@ -174,10 +176,9 @@ async def index(): return res ``` - ::: view.response.Response.cookie -Note that **all response classes inherit from `Response`**, meaning you can use this functionality anywhere. +Note that **all response classes inherit from `Response`**, meaning you can use this functionality anywhere. !!! note @@ -198,12 +199,12 @@ Note that **all response classes inherit from `Response`**, meaning you can use ### Body Translate Strategy -The body translate strategy in the `__view_response__` protocol refers to how the `Response` class will translate the body into a `str`. There are four available strategies: +The body translate strategy in the `__view_result__` protocol refers to how the `Response` class will translate the body into a `str`. There are four available strategies: -- `str`, which uses the object's `__str__` method. -- `repr`, which uses the object's `__repr__` method. -- `result`, which calls the `__view_response__` protocol implemented on the object (assuming it exists). -- `custom`, uses the `Response` instance's `_custom` attribute (this only works on subclasses of `Response` that implement it). +- `str`, which uses the object's `__str__` method. +- `repr`, which uses the object's `__repr__` method. +- `result`, which calls the `__view_result__` protocol implemented on the object (assuming it exists). +- `custom`, uses the `Response` instance's `_custom` attribute (this only works on subclasses of `Response` that implement it). For example, the route below would return the string `"'hi'"`: @@ -241,7 +242,7 @@ from view import Response class ListResponse(Response[list]): def __init__(self, body: list) -> None: - super().__init__(body) + super().__init__(body, body_translate="custom") def _custom(self, body: list) -> str: return " ".join(body) @@ -257,7 +258,7 @@ The main difference between middleware in view.py and other frameworks is that i !!! question "Why no `call_next`?" - view.py doesn't use the `call_next` function because of the nature of it's routing system. + view.py doesn't use the `call_next` function because of the nature of it's routing system. ### The Middleware API @@ -285,10 +286,10 @@ app.run() Responses can be returned with a string, integer, and/or dictionary in any order. -- The string represents the body of the response (e.g. the HTML or JSON) -- The integer represents the status code (200 by default) -- The dictionary represents the headers (e.g. `{"x-www-my-header": "some value"}`) +- The string represents the body of the response (e.g. the HTML or JSON) +- The integer represents the status code (200 by default) +- The dictionary represents the headers (e.g. `{"x-www-my-header": "some value"}`) -`Response` objects can also be returned, which implement the `__view_response__` protocol. All response classes inherit from `Response`, which supports operations like setting cookies. +`Response` objects can also be returned, which implement the `__view_result__` protocol. All response classes inherit from `Response`, which supports operations like setting cookies. Finally, the `middleware` method on a `Route` can be used to implement middleware. diff --git a/docs/index.md b/docs/index.md index e6c3633..175c935 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,66 +1,5 @@ -
view.py logo
+--- +template: home.html -# Welcome to the view.py documentation! - -Here, you can learn how to use view.py and its various features. - -- [Source](https://github.com/ZeroIntensity/view.py) -- [PyPI](https://pypi.org/project/view.py) -- [Discord](https://discord.gg/tZAfuWAbm2) - -!!! warning - - view.py is in very early stages and not yet considered to be ready for production. - If you would like to follow development progress, join [the discord](https://discord.gg/tZAfuWAbm2). - For contributing to view.py, please see our [CONTRIBUTING.md](https://github.com/ZeroIntensity/view.py/blob/master/CONTRIBUTING.md) - -
- -

view.py is affiliated with Space Hosting

-
- -## Quickstart - -Install view.py: - -``` -$ pip install -U view.py -``` - -Initialize your project: - -``` -$ view init -``` - -**Note:** If this yields unexpected results, you may need to use `py -m view init` instead. - -Write your first app: - -```py -from view import new_app - -app = new_app() - -@app.query("greeting", str, default="hello") -@app.query("name", str, default="world") -@app.get("/") -async def index(greeting: str, name: str): - return f"{greeting}, {name}!" - -app.run() -``` - -## Why View? - -As of now, view.py is still in alpha. Lot's of development progress is being made, but a production-ready stable release is still a bit far off. With that being said, anything mentioned in this documentation has been deemed already stable. In that case, why choose view.py over other frameworks? - -If you've used a framework like [Django](https://djangoproject.com), you're likely already familiar with the "batteries included" idea, meaning that it comes with everything you could need right out of the box. View takes a different approach: batteries-detachable. It aims to provide you everything you need, but gives you a choice to use it or not, as well as actively supporting external libraries. This ideology is what makes View special. In batteries detachable, you can use whatever you like right out of the box, but if you don't like View's approach to something or like another library instead, you may easily use it. - -## Should I use it? - -For a big project, **not yet**, as View is not currently ideal for working with a big codebase. However, **that doesn't mean you should forget about it**. view.py will soon be stable and production ready, and you should keep it in mind. To support view.py's development, you can either [sponsor me](https://github.com/sponsors/ZeroIntensity) or [star the project](https://github.com/zerointensity/view.py/stargazers). - -## Developing View - -As stated earlier, view.py is very new and not yet at a stable 1.0.0 release. Whether you're completely new to GitHub contribution or an experienced developer, view.py has something you could help out with. If you're interested in contributing or helping out with anything, be sure to read [the contributors file](https://github.com/ZeroIntensity/view.py/blob/master/CONTRIBUTING.md) and/or joining the [discord](https://discord.gg/tZAfuWAbm2). +title: The Batteries-Detachable Web Framework +--- diff --git a/docs/reference/app.md b/docs/reference/app.md new file mode 100644 index 0000000..3a616d2 --- /dev/null +++ b/docs/reference/app.md @@ -0,0 +1,3 @@ +# App Reference + +::: view.app diff --git a/docs/reference/config.md b/docs/reference/config.md new file mode 100644 index 0000000..3769d15 --- /dev/null +++ b/docs/reference/config.md @@ -0,0 +1,3 @@ +# Configuration Reference + +::: view.config diff --git a/docs/reference/exceptions.md b/docs/reference/exceptions.md new file mode 100644 index 0000000..bbf90b8 --- /dev/null +++ b/docs/reference/exceptions.md @@ -0,0 +1,3 @@ +# Exceptions Reference + +::: view.exceptions diff --git a/docs/reference/responses.md b/docs/reference/responses.md new file mode 100644 index 0000000..d497070 --- /dev/null +++ b/docs/reference/responses.md @@ -0,0 +1,3 @@ +# Responses Reference + +::: view.response diff --git a/docs/reference/routing.md b/docs/reference/routing.md new file mode 100644 index 0000000..2efeb82 --- /dev/null +++ b/docs/reference/routing.md @@ -0,0 +1,6 @@ +# Routing Reference + +::: view.routing + + +::: view._loader diff --git a/docs/reference/templates.md b/docs/reference/templates.md new file mode 100644 index 0000000..209591c --- /dev/null +++ b/docs/reference/templates.md @@ -0,0 +1,3 @@ +# Templating Reference + +::: view.templates diff --git a/docs/reference/types.md b/docs/reference/types.md new file mode 100644 index 0000000..64e8d58 --- /dev/null +++ b/docs/reference/types.md @@ -0,0 +1,3 @@ +# Types Reference + +::: view.typing diff --git a/docs/reference/utils.md b/docs/reference/utils.md new file mode 100644 index 0000000..b802b63 --- /dev/null +++ b/docs/reference/utils.md @@ -0,0 +1,4 @@ +# Utilities Reference + +::: view.util +::: view.typecodes diff --git a/docs/welcome.md b/docs/welcome.md new file mode 100644 index 0000000..e8e7d76 --- /dev/null +++ b/docs/welcome.md @@ -0,0 +1,59 @@ +# Welcome to the view.py documentation! + +Here, you can learn how to use view.py and its various features. + +- [Source](https://github.com/ZeroIntensity/view.py) +- [PyPI](https://pypi.org/project/view.py) +- [Discord](https://discord.gg/tZAfuWAbm2) + +!!! warning + + view.py is in very early stages and not yet considered to be ready for production. + If you would like to follow development progress, join [the discord](https://discord.gg/tZAfuWAbm2). + For contributing to view.py, please see our [CONTRIBUTING.md](https://github.com/ZeroIntensity/view.py/blob/master/CONTRIBUTING.md) + +## Quickstart + +Install view.py: + +``` +$ pip install -U view.py +``` + +Initialize your project: + +``` +$ view init +``` + +**Note:** If this yields unexpected results, you may need to use `py -m view init` instead. + +Write your first app: + +```py +from view import new_app + +app = new_app() + +@app.query("greeting", str, default="hello") +@app.query("name", str, default="world") +@app.get("/") +async def index(greeting: str, name: str): + return f"{greeting}, {name}!" + +app.run() +``` + +## Why View? + +As of now, view.py is still in alpha. Lot's of development progress is being made, but a production-ready stable release is still a bit far off. With that being said, anything mentioned in this documentation has been deemed already stable. In that case, why choose view.py over other frameworks? + +If you've used a framework like [Django](https://djangoproject.com), you're likely already familiar with the "batteries included" idea, meaning that it comes with everything you could need right out of the box. View takes a different approach: batteries-detachable. It aims to provide you everything you need, but gives you a choice to use it or not, as well as actively supporting external libraries. This ideology is what makes View special. In batteries detachable, you can use whatever you like right out of the box, but if you don't like View's approach to something or like another library instead, you may easily use it. + +## Should I use it? + +For a big project, **not yet**, as View is not currently ideal for working with a big codebase. However, **that doesn't mean you should forget about it**. view.py will soon be stable and production ready, and you should keep it in mind. To support view.py's development, you can either [sponsor me](https://github.com/sponsors/ZeroIntensity) or [star the project](https://github.com/zerointensity/view.py/stargazers). + +## Developing View + +As stated earlier, view.py is very new and not yet at a stable 1.0.0 release. Whether you're completely new to GitHub contribution or an experienced developer, view.py has something you could help out with. If you're interested in contributing or helping out with anything, be sure to read [the contributors file](https://github.com/ZeroIntensity/view.py/blob/master/CONTRIBUTING.md) and/or joining the [discord](https://discord.gg/tZAfuWAbm2). diff --git a/html/base.html b/html/base.html index 9ea0760..91bd561 100644 --- a/html/base.html +++ b/html/base.html @@ -1,262 +1,466 @@ + + + + {% if page.title %}{{ page.title }} - {% endif %}{{ config.site_name + }} + + + + + + + + + + {% for js in extra_javascript %} + + {% endfor %} + - - - - {% if page.title %}{{ page.title }} - {% endif %}{{ config.site_name }} - - - - - - - - - - - - - - - - - - - - -
- {% for nav_item in nav %} - {% if nav_item.active %} - {% if nav_item.children %} -