diff --git a/README.md b/README.md
index 69a136f..83dfb79 100644
--- a/README.md
+++ b/README.md
@@ -95,7 +95,7 @@ Here's how to package, test, and ship a new release. (Note that this is [largely
```sh
python3 -m build
setenv ver X.Y
- twine upload -r pypitest dist/lexrpc-$ver.tar.gz
+ twine upload -r pypitest dist/lexrpc-$ver*
```
1. Install from test.pypi.org.
```sh
@@ -142,7 +142,7 @@ Here's how to package, test, and ship a new release. (Note that this is [largely
def ping(input, message=''):
return {'message': message}
- print(server.call('io.example.ping', message='hello world')
+ print(server.call('io.example.ping', {}, message='hello world'))
```
1. Tag the release in git. In the tag message editor, delete the generated comments at bottom, leave the first line blank (to omit the release "title" in github), put `### Notable changes` on the second line, then copy and paste this version's changelog contents below it.
```sh
@@ -152,7 +152,7 @@ Here's how to package, test, and ship a new release. (Note that this is [largely
1. [Click here to draft a new release on GitHub.](https://github.com/snarfed/lexrpc/releases/new) Enter `vX.Y` in the _Tag version_ box. Leave _Release title_ empty. Copy `### Notable changes` and the changelog contents into the description text box.
1. Upload to [pypi.org](https://pypi.org/)!
```sh
- twine upload dist/lexrpc-$ver.tar.gz
+ twine upload dist/lexrpc-$ver*
```
1. [Wait for the docs to build on Read the Docs](https://readthedocs.org/projects/lexrpc/builds/), then check that they look ok.
1. On the [Versions page](https://readthedocs.org/projects/lexrpc/versions/), check that the new version is active, If it's not, activate it in the _Activate a Version_ section.
@@ -160,6 +160,8 @@ Here's how to package, test, and ship a new release. (Note that this is [largely
## Changelog
-### 0.1 - unreleased
+### 0.1 - 2022-12-13
Initial release!
+
+Tested interoperability with the `lexicon`, `xprc`, and `xrpc-server` packages in [bluesky-social/atproto](https://github.com/bluesky-social/atproto). Lexicon and XRPC are still very early and under active development; caveat hacker!
diff --git a/docs/index.rst b/docs/index.rst
index 195f0a1..5c55d11 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -3,95 +3,95 @@ lexrpc
Python implementation of `AT Protocol `__\ ’s
`XRPC `__ +
-`Lexicon `__, both client and
-server. `Docs here. `__
-
-- `Getting started <#getting-started>`__
-
- - `Client <#client>`__
- - `Server <#server>`__
- - `Flask server <#flask-server>`__
-
+`Lexicon `__. lexrpc includes a
+simple `XRPC `__ client, server, and
+`Flask `__ web server integration.
+All three include full `Lexicon `__
+support for validating inputs, outputs, and parameters against their
+schemas.
+
+- `Client <#client>`__
+- `Server <#server>`__
+- `Flask server <#flask-server>`__
+- `Reference `__
- `TODO <#todo>`__
- `Changelog <#changelog>`__
License: This project is placed in the public domain.
-Getting started
----------------
-
-lexrpc includes a simple `XRPC `__
-client, server, and `Flask `__ web
-server integration. All three include full
-`Lexicon `__ support for validating
-inputs, outputs, and parameters against their schemas.
-
Client
-~~~~~~
+------
The lexrpc client let you `call methods dynamically by their
NSIDs `__. To make a
call, first instantiate a
`Client `__
-object with the server address and method lexicons, then use NSIDs to
-make calls, passing input as a dict and parameters as kwargs:
+object with the server address and method lexicons, then use method
+NSIDs to make calls, passing input as a dict and parameters as kwargs:
.. code:: py
- from jsonschema import Client
+ from lexrpc import Client
lexicons = [...]
client = Client('https://xrpc.example.com', lexicons)
- output = client.com.example.my_query({'foo': 'bar'}, a_param=False)
+ output = client.com.example.my_query({'foo': 'bar'}, param_a=5)
Note that ``-`` characters in method NSIDs are converted to ``_``\ s, eg
the call above is for the method ``com.example.my-query``.
Server
-~~~~~~
+------
To implement an XRPC server, use the
`Server `__
class. It validates parameters, inputs, and outputs. Use the
`method `__
-decorator to register handlers for each NSID,
-`Server.call `__
-to call a method from your web framework or anywhere else.
+decorator to register method handlers and
+`call `__
+to call them, whether from your web framework or anywhere else.
.. code:: py
- from jsonschema import Server
+ from lexrpc import Server
lexicons = [...]
server = Server(lexicons)
@server.method('com.example.my-query')
def my_query_hander(input, **params):
- ...
- output = ...
+ output = {'foo': input['foo'], 'b': params['param_a'] + 1}
return output
- # In your web framework, use Server.call to run a method
+ # Extract nsid and decode query parameters from an HTTP request,
+ # call the method, return the output in an HTTP response
+ nsid = request.path.removeprefix('/xrpc/')
input = request.json()
- params = request.query_params()
- # decode params
+ params = server.decode_params(nsid, request.query_params())
output = server.call(input, **params)
response.write_json(output)
Flask server
-~~~~~~~~~~~~
+------------
-First, instantiate a ``Server`` and register method handlers as
-described above. Then, attach the server to your Flask app with
-`lexrpc.init_flask `__.
+To serve XRPC methods in a
+`Flask `__ web app, first install
+the lexrpc package with the ``flask`` extra, eg
+``pip install lexrpc[flask]``. Then, instantiate a
+`Server `__
+and register method handlers as described above. Finally, attach the
+server to your Flask app with
+`flask_server.init_flask `__.
.. code:: py
from flask import Flask
- from lexrpc import init_flask
+ from lexrpc.flask_server import init_flask
+
+ # instantiate a Server like above
+ server = ...
app = Flask('my-server')
- ...
init_flask(server, app)
This configures the Flask app to serve the methods registered with the
@@ -105,20 +105,104 @@ set to ``application/json``.
TODO
----
-- validate records/tokens in input/output? or are those only
- primitives?
-- extensions, https://atproto.com/guides/lexicon#extensibility . is
- there anything to do? ah, it’s currently TODO in the spec:
- https://atproto.com/specs/xrpc#todos
-- “binary blob” support, as in https://atproto.com/specs/xrpc . is it
- currently undefined?
-- authentication, currently TODO in the spec:
- https://atproto.com/specs/xrpc#todos
+- support record types, eg via type “ref” and ref field pointing to the
+ nsid `example
+ here `__,
+ ref points to
+ `app.bsky.actor.ref `__.
+ ref isn’t documented yet though, and these lexicons also use a
+ ``defs`` field, which isn’t really documented either. `they plan to
+ update the docs and specs
+ soon. `__
+- `extensions `__. is
+ there anything to do? ah, `they’re currently TODO in the
+ spec `__.
+- `“binary blob” support. `__ currently
+ undefined ish? is it based on the ``encoding`` field?
+- `authentication, currently TODO in the
+ spec `__
+
+Release instructions
+--------------------
+
+Here’s how to package, test, and ship a new release. (Note that this is
+`largely duplicated in the oauth-dropins readme
+too `__.)
+
+1. Run the unit tests.
+ ``sh source local/bin/activate.csh python3 -m unittest discover``
+
+2. Bump the version number in ``setup.py`` and ``docs/conf.py``.
+ ``git grep`` the old version number to make sure it only appears in
+ the changelog. Change the current changelog entry in ``README.md``
+ for this new version from *unreleased* to the current date.
+
+3. Build the docs. If you added any new modules, add them to the
+ appropriate file(s) in ``docs/source/``. Then run
+ ``./docs/build.sh``. Check that the generated HTML looks fine by
+ opening ``docs/_build/html/index.html`` and looking around.
+
+4. ``git commit -am 'release vX.Y'``
+
+5. Upload to `test.pypi.org `__ for testing.
+ ``sh python3 -m build setenv ver X.Y twine upload -r pypitest dist/lexrpc-$ver*``
+
+6. Install from test.pypi.org.
+ ``sh cd /tmp python3 -m venv local source local/bin/activate.csh pip3 uninstall lexrpc # make sure we force pip to use the uploaded version pip3 install --upgrade pip pip3 install -i https://test.pypi.org/simple --extra-index-url https://pypi.org/simple lexrpc==$ver deactivate``
+
+7. Smoke test that the code trivially loads and runs.
+ ``sh source local/bin/activate.csh python3 # run test code below deactivate``
+ Test code to paste into the interpreter: \`py from lexrpc import
+ Server
+
+ server = Server([{ ‘lexicon’: 1, ‘id’: ‘io.example.ping’, ‘defs’: {
+ ‘main’: { ‘type’: ‘query’, ‘description’: ‘Ping the server’,
+ ‘parameters’: {‘message’: { ‘type’: ‘string’ }}, ‘output’: {
+ ‘encoding’: ‘application/json’, ‘schema’: { ‘type’: ‘object’,
+ ‘required’: [‘message’], ‘properties’: {‘message’: { ‘type’:
+ ‘string’ }}, }, }, }, }, }])
+
+ @server.method(‘io.example.ping’) def ping(input, message=’‘):
+ return {’message’: message}
+
+ print(server.call(‘io.example.ping’, {}, message=‘hello world’))
+ \``\`
+
+8. Tag the release in git. In the tag message editor, delete the
+ generated comments at bottom, leave the first line blank (to omit
+ the release “title” in github), put ``### Notable changes`` on the
+ second line, then copy and paste this version’s changelog contents
+ below it.
+ ``sh git tag -a v$ver --cleanup=verbatim git push && git push --tags``
+
+9. `Click here to draft a new release on
+ GitHub. `__ Enter
+ ``vX.Y`` in the *Tag version* box. Leave *Release title* empty. Copy
+ ``### Notable changes`` and the changelog contents into the
+ description text box.
+
+10. Upload to `pypi.org `__!
+ ``sh twine upload dist/lexrpc-$ver*``
+
+11. `Wait for the docs to build on Read the
+ Docs `__, then
+ check that they look ok.
+
+12. On the `Versions
+ page `__, check
+ that the new version is active, If it’s not, activate it in the
+ *Activate a Version* section.
Changelog
---------
-0.1 - unreleased
+0.1 - 2022-12-13
~~~~~~~~~~~~~~~~
Initial release!
+
+Tested interoperability with the ``lexicon``, ``xprc``, and
+``xrpc-server`` packages in
+`bluesky-social/atproto `__.
+Lexicon and XRPC are still very early and under active development;
+caveat hacker!