diff --git a/README.md b/README.md index c655ef1..7a05a12 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,11 @@ ![compute](https://github.com/kennethreitz42/replit-py/blob/kr-cleanup/ext/readme.gif?raw=true) - - This repository is the home for the `replit` Python package, which provides: -- A fully-featured database client for [Repl.it DB](https://docs.repl.it/misc/database). **[[docs]](https://example.com)** -- A **work in progress** Repl.it user profile lookup. **[[docs]](https://example.com)** -- A Flask application decorator for ensuring Repl.it Auth required on specific routes. **[[docs]](https://example.com)** +- A fully-featured database client for [Replit DB](https://docs.repl.it/misc/database). +- A Flask–based application framework for accellerating development on the platform. +- Replit user profile metadata retreival (more coming here!). & other helpful toys and utilities, like... @@ -16,8 +14,6 @@ This repository is the home for the `replit` Python package, which provides: - Some helpful functions for displaying ANSI colors within interactive terminal sessions. - - ### It's worth noting… diff --git a/TODO b/TODO index 50f244d..d460f22 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,4 @@ - DB: convience methods like nuke and a CLI to interact with it? - User: GraphQL changes on backend to formalize gathering profile information. - User: Add Repl class and info to user +- Proper "Warning: REPLIT_DB_URL does not exist" diff --git a/docs/_static/404.png b/docs/_static/404.png new file mode 100644 index 0000000..fcfb251 Binary files /dev/null and b/docs/_static/404.png differ diff --git a/docs/_static/evalbot.png b/docs/_static/evalbot.png index 277db2f..776ff3f 100644 Binary files a/docs/_static/evalbot.png and b/docs/_static/evalbot.png differ diff --git a/docs/api.rst b/docs/api.rst new file mode 100644 index 0000000..e7aa9e5 --- /dev/null +++ b/docs/api.rst @@ -0,0 +1,51 @@ +API Reference +------------- + +This section of the documentation serves to provide a reference guide for developers, tinkerers, engieneers, and explorers for the Replit-py API, and showcases the function and method calls available within the library. + +.. automodule:: replit + :members: + :undoc-members: + :show-inheritance: + + +Replit Web Framework & Utilities +-------------------------------- + +.. automodule:: replit.web.app + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: replit.web + :members: + :undoc-members: + :show-inheritance: + + +Replit DB +--------- + +.. automodule:: replit.database + :members: + :undoc-members: + :show-inheritance: + +Replit Profile Information +-------------------------- + +.. automodule:: replit.users + :members: + :undoc-members: + :show-inheritance: + + + + +replit.audio module +------------------- + +.. automodule:: replit.audio + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/conf.py b/docs/conf.py index 94fa4d7..d228087 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -16,6 +16,8 @@ import sys sys.path.insert(0, os.path.abspath("../src")) sys.path.append(os.path.abspath("../src")) +import replit + # -- Project information ----------------------------------------------------- @@ -67,7 +69,7 @@ html_theme_options = { html_sidebars = { 'index': ['sidebarintro.html', 'sourcelink.html', 'searchbox.html', 'hacks.html'], - '**': ['sidebarlogo.html', 'localtoc.html', 'relations.html', + '**': ['sidebarintro.html', 'localtoc.html', 'relations.html', 'sourcelink.html', 'searchbox.html', 'hacks.html'] } diff --git a/docs/index.rst b/docs/index.rst index a1690e4..7153ee4 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -3,18 +3,18 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -Welcome to Replit-py's API Guide! -================================= +Welcome to Replit-py's User Guide! +================================== -.. figure:: https://github.com/kennethreitz42/replit-py/blob/kr-cleanup/ext/readme.gif?raw=true +\>\>\> ``import replit`` +~~~~~~~~~~~~~~~~~~~~~~~~ This repository is the home for the ``replit`` Python package, which provides: -- A fully-featured database client for `Repl.it DB`_. `[docs]`_ -- A **work in progress** Repl.it user profile lookup. `[docs]`_ -- A Flask application decorator for ensuring Repl.it Auth required on - specific routes. `[docs]`_ +- A fully-featured database client for `Replit DB`_. +- A Flask–based application framework for accellerating development on the platform. +- Replit user profile metadata retreival (more coming here!). & other helpful toys and utilities, like… @@ -22,63 +22,197 @@ provides: - Some helpful functions for displaying ANSI colors within interactive terminal sessions. -It’s worth noting… -~~~~~~~~~~~~~~~~~~ -The `Repl.it`_ Python environment does not require any platform-specific +.. figure:: https://github.com/kennethreitz42/replit-py/blob/kr-cleanup/ext/readme.gif?raw=true + +Something to note… +------------------ + +The Replit Python environment does not require any platform-specific code, however, these optional utilities provide additional platform features in a simple and accessible way. -*Example*: `Repl.it DB`_ is an HTTP service, but an optional Python +*Example*: `Replit DB`_ is an HTTP service, but an optional Python client (here!) is available. -.. _Repl.it DB: https://docs.repl.it/misc/database -.. _[docs]: https://example.com +.. _Replit DB: https://docs.repl.it/misc/database .. _Repl.it: https://repl.it/ + +Building Websites and APIs +========================== + +.. note:: This guide assumes that you are familiar with running code of some kind on Replit. If you aren’t, please refer to our `Quick Start Guide `_. + +In this tutorial, we are you going to build a Web Service – a process +that responds to incoming HTTP Requests, like the ones that come from +web browsers or even API Clients. + +- HTTP, if you aren’t familiar, is a protocol that allows modern + machines (and users of them) to interoperate. You use HTTP every time + you open an app on your phone that talks with the web. If you can’t + use an app on your phone without internet, it’s because it’s talking + to another computer (a server), mostly likely with HTTP. + +The Basics +---------- + +Code can operate with HTTP in one of two ways: + +1. Code can serve HTTP requests. +2. Code can consume HTTP responses. + +These operations are not mutually exclusive, however. A web applications +can (and often do) perform both operations simultaneously. An example of +this is when a website serving incoming requests makes external calls to +backing APIs it is using to power it’s user experience. You’ll see an +example of this in the working demo we’ll link to in the end! + +HTTP Requests +~~~~~~~~~~~~~ + +If you think you aren’t familiar with the concept of an actual HTTP +request, you may actually be mistaken! Here’s an overt example: + +.. image:: _static/404.png + +Every HTML page you visit is a distinct HTTP response. However, this one +is remarkable in that it is a 404 page. The ‘404: Not Found’ is known as +the HTTP Status Code. There’s a full list of them: + +`List of HTTP status codes - Wikipedia`_ + +The relevant ones are divided into four major categories, distinguished +by the first number: + +- ``2xx``: Success! +- ``3xx``: Redirection — *a URL moved*. +- ``4xx``: Client Errors — *something went wrong on the consumption side*. +- ``5xx``: Server Errors — *something went wrong on the server side*. + +There’s a well-defined standard that declares these codes, and it is +known as `RFC 2616`_. + +HTTP on Replit +-------------- + +The process of receiving incoming HTTP requests on Replit is very +straight forward: you simply open a port that is bound to the ``0.0.0.0`` +host. That’s it! Replit will automatically give you a domain name, and +the interface will present you with a simple browser window that you can +check your frontend with. + +The URL of your application (e.g. ``https://..repl.co``) is publicly accessible. + +.. _Repl.it: http://repl.it/ +.. _List of HTTP status codes - Wikipedia: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes +.. _RFC 2616: https://tools.ietf.org/html/rfc2616 + +.. _code-samples: +.. :ref:`installed ` + +Code Samples +============ + +Here are a carefully curated collection of code samples for utilizing the ``replit`` Python library: + +Hello, World! +~~~~~~~~~~~~~ + +Here's a basic "Hello, World!" example, using the web framework provided: + +:: + + from replit import web + + app = web.App(__name__) + + @app.route("/") + def index(): + return "Hello, World!" + + app.run() + +Adding Required Replit Login +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In this example, we are requiring that a Replit user sign in with their Replit user account in order to see the "Hello, World!": + +:: + + from replit import web + + app = web.App(__name__) + + @web.needs_sign_in(login_res=f"Hello! {web.login_snippet}") + def index(): + return "Hello, World!" + + app.run() + + +Getting a User's Profile Information +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In this next example, we will take the logged in user's profile information and present it in API form: + +:: + + from replit import web, get_profile + + app = web.App(__name__) + + @web.needs_sign_in(login_res=f"Hello! {web.login_snippet}") + def index(): + username = web.whoami() + + payload = {} + payload["greetings"] = True + payload["profile"] = get_profile(username).as_dict + + return payload + + app.run() + +Here's the example API response: + +:: + + $ curl https://replit-py-code-sample.kennethreitz42.repl.co/ + { + "greetings": true, + "profile": { + "avatar_url": "https://storage.googleapis.com/replit/images/1609937177761_8ce62c49dfd5f0192cff5d3684f78a21.jpeg", + "bio": "Software Engineer focused on abstractions, reducing cognitive overhead, and Design for Humans.", + "name": "Kenneth Reitz", + "username": "kennethreitz42" + } + } + +Pretty useful! + +You could use this to build your own social tools based around the Replit community. + + +ReplTweet Example +~~~~~~~~~~~~~~~~~ + +A more fully–fledged example is a Repl we have available known as ReplTweet. It is a Twitter clone! + +- `https://repl.it/@kennethreitz42/repltweet `_ +- `https://github.com/replit/example-repltweet `_ + +Check out the Repl and the GitHub repo to see it in action and learn more about how to use +this library to your full advantage! + + +API Reference +============= + .. toctree:: :maxdepth: 2 - :caption: Contents: -Module contents ---------------- - -.. automodule:: replit - :members: - :undoc-members: - :show-inheritance: - -replit.database module ----------------------- - -.. automodule:: replit.database - :members: - :undoc-members: - :show-inheritance: - -replit.audio module -------------------- - -.. automodule:: replit.audio - :members: - :undoc-members: - :show-inheritance: - -replit.users module -------------------- - -.. automodule:: replit.users - :members: - :undoc-members: - :show-inheritance: - -replit.maqpy module -------------------- - -.. automodule:: replit.web - :members: - :undoc-members: - :show-inheritance: + api Indices and tables ================== diff --git a/docs/web.rst b/docs/web.rst new file mode 100644 index 0000000..44a1d9a --- /dev/null +++ b/docs/web.rst @@ -0,0 +1,8 @@ +Existing code on the platform + +| \| \| \| \| +| \| + +Abstract: What I’d like from you is to write a guide about building and +hosting apps with `Repl.it`_ and Python. + diff --git a/poetry.lock b/poetry.lock index c6d2e72..5be8ca8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -869,7 +869,7 @@ multidict = ">=4.0" [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "c0418f83b4667563684b1c4a2bcb47972d39d1d3b9ed4be8e86235eb7954e66d" +content-hash = "4aaadc3f7efe2f180b21a75fcd9302786e862d32ef0a104fa425ad2633dc8e3e" [metadata.files] aiohttp = [ diff --git a/pyproject.toml b/pyproject.toml index 62fef9a..84fd045 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,6 +32,7 @@ sphinx-autodoc-typehints = "^1.11.0" sphinx-rtd-theme = "^0.5.0" coverage = "^5.2.1" mypy = "^0.782" +Flask = "^1.1.2" [build-system] requires = ["poetry>=0.12"] diff --git a/src/replit/termutils.py b/src/replit/termutils.py index 4fd858b..cd79c95 100644 --- a/src/replit/termutils.py +++ b/src/replit/termutils.py @@ -97,7 +97,7 @@ class Color: color : RGB colors from Hex Value """ try: - r, g, b = colorsys.hls_to_rgb(h, s, v) + r, g, b = colorsys.hls_to_rgb(h, s, l) except: raise ValueError("Converting HLS to RGB ran into an error") diff --git a/src/replit/users.py b/src/replit/users.py index 2e58b62..ad342b2 100644 --- a/src/replit/users.py +++ b/src/replit/users.py @@ -14,10 +14,15 @@ html_http = HTMLSession() class ReplitUser: + """A Replit user's petadata.""" def __init__(self, username=None): + #: The profile's username. self.username = username + #: The profile's given name. self.name = None + #: The profile's given bio. self.bio = None + #: The URL to the profile's avatar image. self.avatar_url = None def __repr__(self): @@ -25,6 +30,7 @@ class ReplitUser: @property def as_dict(self): + """The metadata of the given profile, as a dictionary.""" return { "username": self.username, "name": self.name, @@ -34,6 +40,7 @@ class ReplitUser: @staticmethod def _replit_url_from_username(username): + """Construct a Replit URL from a username.""" return f"https://{REPLIT_DOMAIN}/@{username}" @classmethod @@ -59,6 +66,7 @@ class ReplitUser: @property def avatar_content(self): + """The binary content of the user profile's avatar.""" if self.avatar_url: return http.get(self.avatar_url).content diff --git a/src/replit/web/app.py b/src/replit/web/app.py index 79e0a74..addd1d3 100644 --- a/src/replit/web/app.py +++ b/src/replit/web/app.py @@ -1,5 +1,3 @@ -"""Core of flask_tools.""" - from dataclasses import dataclass from functools import wraps from pathlib import Path diff --git a/src/replit/web/utils.py b/src/replit/web/utils.py index 4917d92..29ed442 100644 --- a/src/replit/web/utils.py +++ b/src/replit/web/utils.py @@ -10,7 +10,7 @@ from werkzeug.local import LocalProxy from .html import Page -sign_in_snippet = ( +authentication_snippet = ( '' ) @@ -28,7 +28,7 @@ def sign_in(title: str = "Please Sign In") -> Page: Returns: Page: The sign-in page. """ - return Page(title=title, body=sign_in_snippet) + return Page(title=title, body=authentication_snippet) sign_in_page = sign_in() @@ -245,3 +245,7 @@ def chain_decorators(*decorators: Callable[[Callable], Any]) -> Callable: if not decorators: raise TypeError("You must provide at least one decorator to chain") return dec + +# Syntax sugar. +sign_in_snippet = authentication_snippet +login_snippet = authentication_snippet