From 9eaf80974d8347f709d62583f759074765340b78 Mon Sep 17 00:00:00 2001 From: Oliver Sanders Date: Tue, 12 Sep 2023 17:25:33 +0100 Subject: [PATCH 1/4] Merge pull request #505 from MetRonnie/flake8-mypy Drop Python 3.7 in flake8/mypy --- mypy.ini | 2 +- tox.ini | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/mypy.ini b/mypy.ini index 3fb1c51f..72700dda 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1,5 +1,5 @@ [mypy] -python_version = 3.7 +python_version = 3.8 ignore_missing_imports = True files = cylc/uiserver # don't run mypy on these files directly diff --git a/tox.ini b/tox.ini index 4c26bb00..91d4b7fb 100644 --- a/tox.ini +++ b/tox.ini @@ -8,9 +8,6 @@ ignore= W503, ; line break after binary operator W504 - ; suggests using f"{!r}" instead of manual quotes (flake8-bugbear) - ; Doesn't work at 3.7 - B028 exclude= build, From 596580994610084a9c430932bffc1a086256ec06 Mon Sep 17 00:00:00 2001 From: Hilary James Oliver Date: Thu, 14 Sep 2023 09:13:22 +1200 Subject: [PATCH 2/4] Merge pull request #493 from markgrahamdawson/feature-add-scan-mutation Feature add scan mutation --- CONTRIBUTING.md | 1 + cylc/uiserver/resolvers.py | 20 +++++++++++++++++--- cylc/uiserver/schema.py | 12 ++++++++++++ cylc/uiserver/tests/test_authorise.py | 3 +++ 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5b68ad6a..6de00e76 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -58,6 +58,7 @@ below. - David Matthews - Mel Hall - Christopher Bennett + - Mark Dawson (All contributors are identifiable with email addresses in the git version diff --git a/cylc/uiserver/resolvers.py b/cylc/uiserver/resolvers.py index 3e8251da..e1e85e47 100644 --- a/cylc/uiserver/resolvers.py +++ b/cylc/uiserver/resolvers.py @@ -279,6 +279,15 @@ async def clean( await workflows_mgr.scan() return cls._return("Workflow(s) cleaned") + @classmethod + async def scan( + cls, + args: dict, + workflows_mgr: 'WorkflowsManager', + ): + await workflows_mgr.scan() + return cls._return("Scan requested") + @classmethod async def play(cls, workflows, args, workflows_mgr, log): """Calls `cylc play`.""" @@ -534,9 +543,10 @@ async def service( info: 'ResolveInfo', command: str, workflows: Iterable['Tokens'], - kwargs: Dict[str, Any] + kwargs: Dict[str, Any], ) -> List[Union[bool, str]]: - if command == 'clean': + + if command == 'clean': # noqa: SIM116 return await Services.clean( workflows, kwargs, @@ -544,7 +554,6 @@ async def service( log=self.log, executor=self.executor ) - elif command == 'play': return await Services.play( workflows, @@ -552,6 +561,11 @@ async def service( self.workflows_mgr, log=self.log ) + elif command == 'scan': + return await Services.scan( + kwargs, + self.workflows_mgr + ) raise NotImplementedError() diff --git a/cylc/uiserver/schema.py b/cylc/uiserver/schema.py index cb00726b..3deb6237 100644 --- a/cylc/uiserver/schema.py +++ b/cylc/uiserver/schema.py @@ -270,6 +270,17 @@ class Arguments: result = GenericScalar() +class Scan(graphene.Mutation): + class Meta: + description = sstrip(''' + Scan the filesystem for file changes. + + Valid for: stopped workflows. + ''') + resolver = partial(mutator, command='scan') + result = GenericScalar() + + async def get_jobs(root, info, **kwargs): if kwargs['live']: return await get_nodes_all(root, info, **kwargs) @@ -545,6 +556,7 @@ class Logs(graphene.ObjectType): class UISMutations(Mutations): play = _mut_field(Play) clean = _mut_field(Clean) + scan = _mut_field(Scan) schema = graphene.Schema( diff --git a/cylc/uiserver/tests/test_authorise.py b/cylc/uiserver/tests/test_authorise.py index 9915e604..73a93a8e 100644 --- a/cylc/uiserver/tests/test_authorise.py +++ b/cylc/uiserver/tests/test_authorise.py @@ -42,6 +42,7 @@ "reload", "remove", "resume", + "scan", "set_graph_window_extent", "set_hold_point", "set_outputs", @@ -66,6 +67,7 @@ "reload", "remove", "resume", + "scan", "set_graph_window_extent", "set_hold_point", "set_outputs", @@ -126,6 +128,7 @@ "play", "trigger", "resume", + "scan", "set_verbosity", "set_graph_window_extent", "read", From 255ca79b74178a96a592f28fda2b022b8d56c3bc Mon Sep 17 00:00:00 2001 From: Mark Dawson Date: Wed, 25 Oct 2023 13:04:29 +0100 Subject: [PATCH 3/4] Extend userprofile endpoint with all data on the current user from Jupyter Server (#510) Extend userprofile endpoint with all data on the current user from Jupyter Server --- cylc/uiserver/handlers.py | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/cylc/uiserver/handlers.py b/cylc/uiserver/handlers.py index 0ec9341e..daa6930f 100644 --- a/cylc/uiserver/handlers.py +++ b/cylc/uiserver/handlers.py @@ -122,7 +122,17 @@ def _authorise( return False -def get_username(handler: 'CylcAppHandler'): +def get_initials(username: str): + if ('.' in username): + first, last = username.split('.', maxsplit=1) + return f"{first[0]}{last[0]}".upper() + elif (username != ''): + return username[0].upper() + else: + return None + + +def get_user_info(handler: 'CylcAppHandler'): """Return the username for the authenticated user. If the handler is token authenticated, then we return the username of the @@ -130,9 +140,16 @@ def get_username(handler: 'CylcAppHandler'): """ if is_token_authenticated(handler): # the bearer of the token has full privileges - return ME + return {'name': ME, 'initials': get_initials(ME), 'username': ME} else: - return handler.current_user.username + initials = handler.current_user.initials or get_initials( + handler.current_user.username + ) + return { + 'name': handler.current_user.name, + 'initials': initials, + 'username': handler.current_user.username + } class CylcAppHandler(JupyterHandler): @@ -247,7 +264,8 @@ def set_default_headers(self) -> None: # @authorised TODO: I can't think why we would want to authorise this def get(self): user_info = { - 'name': get_username(self) + **self.current_user.__dict__, + **get_user_info(self) } # add an entry for the workflow owner @@ -316,7 +334,7 @@ def context(self): 'graphql_params': self.graphql_params, 'request': self.request, 'resolvers': self.resolvers, - 'current_user': get_username(self), + 'current_user': get_user_info(self)['username'], } @web.authenticated # type: ignore[arg-type] @@ -384,7 +402,7 @@ def context(self): return { 'request': self.request, 'resolvers': self.resolvers, - 'current_user': get_username(self), + 'current_user': get_user_info(self)['username'], 'ops_queue': {}, 'sub_statuses': self.sub_statuses } From 5f5373649fe6dcd25b06d4164c41b273d5dbcc91 Mon Sep 17 00:00:00 2001 From: Mark Dawson Date: Wed, 18 Oct 2023 10:00:16 +0100 Subject: [PATCH 4/4] added flake8-type-checking (#513) --- cylc/uiserver/handlers.py | 7 ++++--- mypy.ini | 4 ++++ setup.cfg | 1 + 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/cylc/uiserver/handlers.py b/cylc/uiserver/handlers.py index daa6930f..1ec87421 100644 --- a/cylc/uiserver/handlers.py +++ b/cylc/uiserver/handlers.py @@ -38,10 +38,11 @@ ) from cylc.uiserver.authorise import Authorization, AuthorizationMiddleware -from cylc.uiserver.resolvers import Resolvers from cylc.uiserver.websockets import authenticated as websockets_authenticated -from cylc.uiserver.websockets.tornado import TornadoSubscriptionServer + if TYPE_CHECKING: + from cylc.uiserver.resolvers import Resolvers + from cylc.uiserver.websockets.tornado import TornadoSubscriptionServer from graphql.execution import ExecutionResult @@ -97,7 +98,7 @@ def is_token_authenticated(handler: 'CylcAppHandler') -> bool: In these cases the bearer of the token is awarded full privileges. """ identity_provider: JPSIdentityProvider = ( - handler.serverapp.identity_provider + handler.serverapp.identity_provider # type: ignore[union-attr] ) return identity_provider.__class__ == PasswordIdentityProvider # NOTE: not using isinstance to narrow this down to just the one class diff --git a/mypy.ini b/mypy.ini index 72700dda..060c7e52 100644 --- a/mypy.ini +++ b/mypy.ini @@ -14,3 +14,7 @@ explicit_package_bases = True allow_redefinition = True strict_equality = True show_error_codes = True + +# Suppress the following messages: +# By default the bodies of untyped functions are not checked, consider using --check-untyped-defs +disable_error_code = annotation-unchecked \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index a0229f15..60f300d3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -93,6 +93,7 @@ tests = flake8-debugger>=4.0.0 flake8-mutable>=1.2.0 flake8-simplify>=0.14.0 + flake8-type-checking flake8>=3.0.0 jupyter_server[test] mypy>=0.900