Skip to content

Commit

Permalink
use ruff for pre-commit and run it across all files
Browse files Browse the repository at this point in the history
  • Loading branch information
s-cork committed Oct 18, 2024
1 parent a087d08 commit 5a30dbd
Show file tree
Hide file tree
Showing 21 changed files with 94 additions and 74 deletions.
29 changes: 5 additions & 24 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,14 @@ repos:
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/psf/black
rev: 23.3.0 # Replace by any tag/version: https://github.com/psf/black/tags
hooks:
- id: black
args: # arguments to configure black
- --line-length=88
language_version: python3 # Should be a command that runs python3.6+


- repo: https://github.com/pycqa/isort
rev: 5.12.0
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.3.4
hooks:
- id: isort
name: isort (python)
- id: ruff
args: [--fix]
- id: ruff-format

# flake8
- repo: https://github.com/PyCQA/flake8
rev: 6.0.0
hooks:
- id: flake8
args: # arguments to configure flake8
# making isort line length compatible with black
- "--max-line-length=88"
# these are errors that will be ignored by flake8
# check out their meaning here
# https://flake8.pycqa.org/en/latest/user/error-codes.html
- "--ignore=E203,E266,E501,W503,F403,F401,E402,W605,E302"
- repo: https://github.com/Lucas-C/pre-commit-hooks
rev: v1.5.1
hooks:
Expand Down
12 changes: 12 additions & 0 deletions .ruff.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
src = ["client_code", "server_code"]

[lint]
select = ["E", "F", "I001"]

[lint.isort]
known-third-party = ["anvil"]

[lint.pycodestyle]
# E501 reports lines that exceed the length of 100.
# this only happens when we can't wrap the line with ruff-format
max-line-length = 100
1 change: 1 addition & 0 deletions client_code/atomic/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: MIT
# Copyright (c) 2021 anvilistas

# ruff: noqa: F401
from .atoms import DictAtom, ListAtom, atom, portable_atom
from .contexts import ignore_updates
from .decorators import (
Expand Down
12 changes: 7 additions & 5 deletions client_code/atomic/atoms.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
class BaseAction(
namedtuple("_BaseAction", ["action", "atom", "prop", "value"], defaults=[None])
):
"""We use this as something we can pass to the action queue and might be consumed by a decorated subscriber"""
"""We use this as something we can pass to the action queue
and might be consumed by a decorated subscriber"""

def __str__(self):
action, atom, prop, val = self
Expand All @@ -28,9 +29,9 @@ def __str__(self):


def as_atom(atom, prop, val):
if type(val) is dict:
if type(val) is dict: # noqa: E721
return DictAtom(val)
elif type(val) is list:
elif type(val) is list: # noqa: E721
return ListAtom(atom, prop, val)
else:
return val
Expand Down Expand Up @@ -106,7 +107,7 @@ def portable_atom(_cls, name=None):
"""decorator to for atoms that you also want to be portable classes"""
if IS_SERVER_SIDE:
return portable_class(_cls, name)
elif name is None and type(_cls) is str:
elif name is None and isinstance(_cls, str):
name = _cls
return lambda _cls: portable_atom(_cls, name)

Expand Down Expand Up @@ -237,7 +238,8 @@ def fn(self, *args, **kws):

class ListAtom(list):
"""
Any time the list is mutated we request a render from the parent atom at the property this list belongs to
Any time the list is mutated we request a render from the parent atom
at the property this list belongs to
"""

__slots__ = ["_as_atom", "_request_render", "_register"]
Expand Down
18 changes: 12 additions & 6 deletions client_code/atomic/contexts.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# SPDX-License-Identifier: MIT
# Copyright (c) 2021 anvilistas

from functools import partial

from .constants import ACTION, IGNORE, REACTION, RENDER, SELECTOR
from .rendering import active, call_queued, log, queued
Expand Down Expand Up @@ -78,8 +77,11 @@ class ActionContext(Context):
mode = ACTION

def adder(self):
msg = "Cannot update an Atom or call an action from inside a selector or render method \
- use `with ignore_updates:` if you really need to update an Atom attribute"
msg = (
"Cannot update an Atom or call an action from inside a selector or"
"render method - use `with ignore_updates:`"
" if you really need to update an Atom attribute"
)
self.add_active((SELECTOR, RENDER, REACTION), msg)
queued[ACTION] += (self.context,)

Expand Down Expand Up @@ -112,12 +114,16 @@ def adder(self):

class ReactionContext(Context):
# note the ReactionContext only applies to the depends_on_fn call
# There should only be attribute access and selector method calls within this context
# There should only be attribute access
# and selector method calls within this context
mode = REACTION

def adder(self):
msg = "The reaction depends_on_fn should only access atom attributes.\
Calling a depends_on_fn from inside a selector, render or other depends_on_fn is invalid"
msg = (
"The reaction depends_on_fn should only access atom attributes."
"Calling a depends_on_fn from inside a selector,"
" render or other depends_on_fn is invalid"
)
self.add_active((SELECTOR, RENDER, REACTION), msg)

popper = Context.pop_active
14 changes: 9 additions & 5 deletions client_code/atomic/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ def _get_selector(fn, atom, prop):


def selector(fn):
"""decorate a method as a selector whenever it needs to do some computation based on atom attributes
"""decorate a method as a selector whenever it needs to do
some computation based on atom attributes
This decorate can only be used on an atom method
You should never update an atom within a selector
A selector can be decorated with @property
Expand Down Expand Up @@ -99,7 +100,8 @@ def unsubscribe(f):
class render:
"""a decorator typically used above a method in a form
if used on a form render methods will only execute on the show event
if used as a top level function it can be used to update a database whenever an atom attribute changes
if used as a top level function it can be used to update a database
whenever an atom attribute changes
a render should access all selectors and atom attributes that it depends on
i.e. don't access some attributes within branching logic (if statements)
Expand Down Expand Up @@ -148,12 +150,14 @@ def reaction(
include_previous=False,
):
"""a reaction takes two arguments: depends_on_fn and then_react_fn
the depends_on_fn is used to determine the dependcies that the then_react_fn depends on
the depends_on_fn is used to determine the dependencies that the then_react_fn depends on
when ever an atom attribute accessed in the depends_on_fn changes the then_react_fn is called.
If the depends_on_fn returns a value other than None the return value will be passed to the then_react_fn.
If the depends_on_fn returns a value other than None
the return value will be passed to the then_react_fn.
depends_on_fn fires immediately, but then_react_fn will only be called the next time a dependency changes.
depends_on_fn fires immediately, but then_react_fn will only be called
the next time a dependency changes.
To call the then_react_fn function immediately set fire_immediately to True.
Returns: a dispose function - when called stops any future reactions
Expand Down
15 changes: 10 additions & 5 deletions client_code/atomic/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@ def set_debug(is_debug=True):

def writeback(component, prop, atom_or_selector, attr_or_action=None, events=()):
"""create a writeback between a component property and an atom attribute
or bind the property to an atom selector and call an action when the component property is changed
or bind the property to an atom selector and call an action when
the component property is changed
events - should be a single event str or a list of events
If no events are provided this is the equivalent of a data-binding with no writeback
"""
atom, attr = atom_or_selector, attr_or_action
if type(events) is str:
if isinstance(events, str):
events = [events]
if isinstance(atom, dict):
assert attr is not None, "if a dict atom is provided the attr must be a str"
Expand Down Expand Up @@ -58,9 +59,13 @@ def _noop():


def bind(component, prop, atom_or_selector, attr=SENTINEL):
"""create a data-binding between an component property and an atom and its attribute (or a selector)"""
# we could support methods here but it's better to be explicit and call selectors inside render methods
# accessing an atom property necessarily creates a depenedency on the current render context
"""
create a data-binding between an component property
and an atom and its attribute (or a selector)
"""
# we could support methods here but it's better to be explicit
# and call selectors inside render methods
# accessing an atom property necessarily creates a dependency on the current render context
# so better not to encourage accessing a selector outside of the desired render context
attr = _noop if attr is SENTINEL else attr
return writeback(component, prop, atom_or_selector, attr)
3 changes: 2 additions & 1 deletion client_code/atomic/registrar.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@


class AtomRegistrar:
"""the registrar is responsible for registering and unregistering atom props to renderers/selectors
"""the registrar is responsible for registering and un-registering
atom props to renderers/selectors
as well as caching selectors associated with an atom"""

def __init__(self, atom):
Expand Down
6 changes: 4 additions & 2 deletions client_code/atomic/rendering.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ def log(fn):

def register(atom, prop):
"""if there is an active selector or render
we asks the atom registrar to register a relationship between an atom and the attribute being accessed
we asks the atom registrar to register a relationship
between an atom and the attribute being accessed
"""
if active[IGNORE]:
return
Expand Down Expand Up @@ -91,7 +92,8 @@ def queue_subscribers(atom_registrar, prop, mode):


def request(atom, prop):
"""when an attribute of an atom is accessed we update the queues based on the subscribers registered"""
"""when an attribute of an atom is accessed
we update the queues based on the subscribers registered"""
if active[IGNORE]:
return
atom_registrar = get_registrar(atom)
Expand Down
3 changes: 2 additions & 1 deletion client_code/atomic/subscribers.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@ def compute_cached(self):

def __call__(self, *args, **kws):
# anytime our value is requested make renders/selectors depend on our property
# we don't use atom's __getattribute__ for registration since it doesn't register methods accessed
# we don't use atom's __getattribute__ for registration
# since it doesn't register methods accessed
# and we only want the registration to occur when we call the selector
# this allows selectors to be used as the bind/writeback function
register(self.atom, self.prop)
Expand Down
10 changes: 6 additions & 4 deletions client_code/cluegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,12 @@ def __init_subclass__(cls):
def __init__(cls):
clues = all_clues(cls)
args = ", ".join(
f"{name}={getattr(cls,name)!r}"
if hasattr(cls, name)
and not isinstance(getattr(cls, name), MemberDescriptorType)
else name
(
f"{name}={getattr(cls, name)!r}"
if hasattr(cls, name)
and not isinstance(getattr(cls, name), MemberDescriptorType)
else name
)
for name in clues
)
body = "\n".join(f" self.{name} = {name}" for name in clues)
Expand Down
6 changes: 4 additions & 2 deletions client_code/fido.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ def verify_registration(response):


def register_device():
"""a logged in user is required to register a device. The user table must have a 'fido' simple object column"""
"""a logged in user is required to register a device.
The user table must have a 'fido' simple object column"""
public_key = generate_registration()
try:
resposne = startRegistration(public_key)
Expand All @@ -44,7 +45,8 @@ def verify_authentication_options(authentication_options):


def login_with_fido(email: str):
"""provide an email address to login with fido - this email might be stored in indexed db or local storage or similar"""
"""provide an email address to login with fido
this email might be stored in indexed db or local storage or similar"""
authentication_options = generate_authentication_options(email)
result = verify_authentication_options(authentication_options)
if result is None:
Expand Down
2 changes: 2 additions & 0 deletions client_code/kompot/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# SPDX-License-Identifier: MIT
# Copyright (c) 2021 anvilistas

# ruff: noqa: F401
from ._batcher import batch_call
from ._register import register
from ._rpc import call, call_async, call_s, callable
Expand Down
1 change: 0 additions & 1 deletion client_code/kompot/_register.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
# Copyright (c) 2021 anvilistas

from anvil.server import SerializationError

from anvil_extras.utils import import_module

__version__ = "0.0.1"
Expand Down
1 change: 0 additions & 1 deletion client_code/kompot/_rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from functools import wraps as _wraps

import anvil.server as _server
from anvil import is_server_side

from ._serialize import UNHANDLED, reconstruct, serialize

Expand Down
13 changes: 9 additions & 4 deletions client_code/non_blocking.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,10 @@ def __init__(self, fn, delay=None):
if not _warning:
_warning = True
print(
"WARNING: Interval and Timeout have been deprecated in favour of repeat() and defer() and will soon be removed."
"\nSee latest documentation: https://anvil-labs.readthedocs.io/en/latest/guides/modules/non_blocking.html"
"WARNING: Interval and Timeout have been deprecated in favour of"
" repeat() and defer() and will soon be removed."
"\nSee latest documentation:"
" https://anvil-labs.readthedocs.io/en/latest/guides/modules/non_blocking.html"
)
assert callable(
fn
Expand Down Expand Up @@ -234,7 +236,10 @@ def cancel(ref):
if ref is None:
return
if not isinstance(ref, TimerRef):
msg = "Invalid argumnet to cancel(), expected None or the return value from calling delay/defer"
msg = (
"Invalid argumnet to cancel(),"
" expected None or the return value from calling delay/defer"
)
raise TypeError(msg)
return ref.cancel()

Expand Down Expand Up @@ -346,5 +351,5 @@ def _f(v):
else:
assert False
_v = call_async(lambda: {}).await_result()
assert type(_v) is dict
assert isinstance(_v, dict)
print("PASSED")
2 changes: 1 addition & 1 deletion client_code/web_worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def portable_class(cls,name=None):
(${j})();
})
`;function L(){let e={resolve:()=>{},reject:()=>{}};return e.promise=new Promise((t,r)=>{e.resolve=t,e.reject=r}),e}var{misceval:{callsimArray:E,buildClass:H},ffi:{toPy:K},builtin:{RuntimeError:T,str:I,print:N}}=Sk,S=H({__name__:"anvil_labs.web_worker"},()=>{},"WorkerTaskKilled",[T]);function z(e,t,r){let n;if(e==="WorkerTaskKilled")return n=E(S,t),n.traceback=r,n;try{let a=Sk.builtin[e];n=E(a,t.map(s=>K(s))),n.traceback=r!=null?r:[]}catch{let a;try{a=new window[e](...t)}catch{a=new Error(...t)}n=new Sk.builtin.ExternalError(a)}return n}function F(e){let t={};e.currentTask=null,e.stateHandler=()=>{},e.launchTask=(r,n,a)=>{let s=Math.random().toString(36).substring(6);return e.currentTask={fn:r,id:s},e.postMessage({type:"CALL",id:s,fn:r,args:n,kws:a}),t[s]=L(),[s,r,new Promise(o=>o(t[s].promise))]},e.onmessage=async({data:r})=>{var n;switch(r.type){case"OUT":{let{id:a,fn:s}=(n=e.currentTask)!=null?n:{};N([`<worker-task '${s}' (${a})>:`,r.message],["end",I.$empty]);break}case"STATE":{e.stateHandler({...r.state});break}case"RESPONSE":{let{id:a,value:s,errorType:o,errorArgs:i,errorTb:l}=r,c=t[r.id];c?o?(console.debug(`RPC error response ${a}:`,o,i),c.reject(z(o,i,l))):(console.debug(`RPC response ${a}:`,s),c.resolve(s)):console.warn(`Got worker response for invalid call ${r.id}`,r),delete t[r.id],e.currentTask=null;return}case"IMPORT":{let{id:a,filename:s}=r,o=null;try{o=Sk.read(s),typeof o!="string"&&(o=await Sk.misceval.asyncToPromise(()=>o))}catch{if(s.startsWith("app/")){let[i,l,c]=s.split("/");c==="__init__.py"&&l!=="anvil"&&(o="pass")}else o=null}e.postMessage({type:"MODULE",id:a,content:o})}}}}var b=class{constructor(t,r,n,a){this._id=t;this._name=r;this._result=n;this._target=a;_(this,"_handledResult");_(this,"_startTime");_(this,"_state",{});_(this,"_rv",null);_(this,"_err",null);_(this,"_complete",!1);_(this,"_status",null);_(this,"_stateHandler",()=>{});this._startTime=Date.now(),this._handledResult=n,this._target.stateHandler=this._updateState.bind(this),this._result.then(s=>{this._complete=!0,this._status="completed",this._rv=s},s=>{this._complete=!0,s instanceof S?(this._status="killed",this._err=new T("'"+this._name+"' worker task was killed")):(this._status="failed",this._err=s)})}_updateState(t){this._state=t,this._stateHandler(t)}await_result(){return this._result}on_result(t,r){this._handledResult=this._result.then(t),r&&(this._handledResult=this._handledResult.catch(r))}on_error(t){this._handledResult=this._handledResult.catch(t)}on_state_change(t){this._stateHandler=t}get_state(){return this._state}get_id(){return this._id}get_task_name(){return this._name}get_termination_status(){return this._status}get_return_value(){if(this._err)throw this._err;return this._rv}get_error(){if(this._err!==null)throw this._err}get_start_time(){return this._startTime}is_completed(){if(this._err)throw this._err;return this._complete}is_running(){return!this._complete}kill(){this._complete||this._target.postMessage({type:"KILL",id:this._id})}},v=class{constructor(t){_(this,"target");t.startsWith(window.anvilAppMainPackage)||(t=window.anvilAppMainPackage+"."+t);let r=M.replace("{$filename$}",JSON.stringify(t)).replace("self.anvilAppOrigin",JSON.stringify(window.anvilAppOrigin)),n=new Blob([r],{type:"text/javascript"});this.target=new Worker(URL.createObjectURL(n)),F(this.target)}launch_task(t,r,n){if(this.target.currentTask!==null)throw new T("BackgroundWorker already has an active task");let[s,o,i]=this.target.launchTask(t,r,n);return new b(s,o,i,this.target)}};var C;(C=window.anvilLabs)!=null||(window.anvilLabs={});window.anvilLabs.Worker=v;window.anvilLabs.WorkerTaskKilled=S;})();
"""
""" # noqa: E501

_blob = _W.Blob([_script], {type: "text/javascript"})
_src = _W.URL.createObjectURL(_blob)
Expand Down
1 change: 0 additions & 1 deletion server_code/fido_webauthn.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import base64
import json
import urllib.parse
from base64 import b64decode
from functools import wraps

import anvil.server
Expand Down
5 changes: 2 additions & 3 deletions server_code/web_worker_endpoint.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
# SPDX-License-Identifier: MIT
# Copyright (c) 2021 anvilistas

__version__ = "0.0.1"

import anvil.server

from anvil_labs import kompot

__version__ = "0.0.1"


@anvil.server.http_endpoint("/anvil_labs_private_call")
def anvil_labs_private_call(name):
Expand Down
Loading

0 comments on commit 5a30dbd

Please sign in to comment.