App Basics
New Applications
Every view project will have a new_app
call. The simplest app looks like this:
from view import new_app
app = new_app()
new_app
does a few important things:
- Loads the configuration, regardless of whether a config file exists.
- Sets the
App
address for use byget_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
.
Bases: ViewApp
Public view.py app object.
Source code in src/view/app.py
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 |
|
view.app.App.__init__(config: Config) -> None
Parameters:
Name | Type | Description | Default |
---|---|---|---|
config |
Config
|
Configuration object to be used. Automatically generated by |
required |
Source code in src/view/app.py
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 |
|
view.app.App.body(name: str, *tps: type[V], doc: str | None = None, default: V | None | _NoDefaultType = _NoDefault)
Set a body parameter.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
name |
str
|
Name of the parameter. |
required |
tps |
type[V]
|
Types that can be passed to the server. If empty, any is used. |
()
|
doc |
str | None
|
Description of this body parameter. |
None
|
default |
V | None | _NoDefaultType
|
Default value to be used if not supplied. |
_NoDefault
|
Source code in src/view/app.py
416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 |
|
view.app.App.delete(path: str, *, doc: str | None = None)
Set a DELETE route.
Source code in src/view/app.py
340 341 342 |
|
view.app.App.docs(file: str | TextIO | Path | None = None, *, encoding: str = 'utf-8', overwrite: bool = True) -> str | None
Generate documentation for the app.
Source code in src/view/app.py
660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 |
|
view.app.App.get(path: str, *, doc: str | None = None)
Set a GET route.
Source code in src/view/app.py
332 333 334 |
|
view.app.App.load(routes: list[Route] | None = None) -> None
Load the app. This is automatically called most of the time and should only be called manually during manual loading.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
routes |
list[Route] | None
|
Routes to load into the app. |
None
|
Source code in src/view/app.py
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 |
|
view.app.App.options(path: str, *, doc: str | None = None)
Set a OPTIONS route.
Source code in src/view/app.py
352 353 354 |
|
view.app.App.patch(path: str, *, doc: str | None = None)
Set a PATCH route.
Source code in src/view/app.py
344 345 346 |
|
view.app.App.post(path: str, *, doc: str | None = None)
Set a POST route.
Source code in src/view/app.py
336 337 338 |
|
view.app.App.put(path: str, *, doc: str | None = None)
Set a PUT route.
Source code in src/view/app.py
348 349 350 |
|
view.app.App.query(name: str, *tps: type[V], doc: str | None = None, default: V | None | _NoDefaultType = _NoDefault)
Set a query parameter.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
name |
str
|
Name of the parameter. |
required |
tps |
type[V]
|
Types that can be passed to the server. If empty, any is used. |
()
|
doc |
str | None
|
Description of this query parameter. |
None
|
default |
V | None | _NoDefaultType
|
Default value to be used if not supplied. |
_NoDefault
|
Source code in src/view/app.py
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 |
|
view.app.App.run(*, fancy: bool | None = None) -> None
Run the app.
Source code in src/view/app.py
572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 |
|
view.app.App.run_async(loop: asyncio.AbstractEventLoop | None = None) -> None
Run the app in an event loop.
Source code in src/view/app.py
603 604 605 606 607 608 |
|
view.app.App.run_task(loop: asyncio.AbstractEventLoop | None = None) -> asyncio.Task[None]
Run the app as a task.
Source code in src/view/app.py
610 611 612 613 614 615 |
|
view.app.App.run_threaded(*, daemon: bool = True) -> Thread
Run the app in a thread.
Source code in src/view/app.py
597 598 599 600 601 |
|
view.app.App.test()
async
Open the testing context.
Source code in src/view/app.py
622 623 624 625 626 627 628 629 630 |
|
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).
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.
Generally, you're going to want to add an app.run()
to every view.py project, like so:
from view import new_app
app = new_app()
app.run()
This way, if you (or someone else) want to run your code programmatically, they can run it via something like python3 app.py
. It's also more semantically clear that an app is going to start when you run that file.
If you prefer the CLI method, you can just run view serve
and view.py will extract the app from the file itself, ignoring the run()
call.
Note that this behavior is a double-edged sword, so be careful. When calling with run()
, the Python script will never get past that line because the server will run indefinitely, but when using view serve
it proceeds past it just fine since all it's doing is extracting the app
, skipping run()
. For example, take a look at this code:
from view import new_app
app = new_app()
app.run()
print("you called the app with view serve") # this only runs when `view serve` is used
Run the app.
Source code in src/view/app.py
572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 |
|
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
torun()
.
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
.
Getting the App
Circular Imports
If you've worked with big Python projects before, there's a good chance you've run into a circular import error. A circular import error occurs when two modules try to import each other. A view.py example of this problem would most likely be the main app file trying to import a route, but then that route tries to import the app.
Note
The below example uses routing, which if you're reading this for the first time you don't know how to use yet. Focus on the use of the app
variable and not the routing itself.
# app.py
from view import new_app
from routes import my_route
app = new_app()
app.load([my_route])
app.run()
# routes.py
from view import get
from app import app
@app.get("/something")
def something():
return "something"
@get("/")
def index():
return "Hello, view.py"
View gives you a solution to this problem: get_app
. get_app
uses some magic internally to get you your App
instance right then and there, no import required. It works similar to how you would use new_app
:
from view import get_app
app = get_app()
@app.get("/")
def index():
return "..."
Get the last app created by new_app
.
Source code in src/view/app.py
743 744 745 746 747 748 749 750 751 752 753 |
|
Review
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()
.
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.