-
Couldn't load subscription status.
- Fork 0
Add basic auth client setup #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Adds * A mininal async auth client * an unasync script to build a sync client from the above * docker compose set up for an auth server to test against * a test rig that sets up the auth server with users and roles (not used yet) * tests for the current functionality other than error handling code that can't get be reached * eventual publishing on pypi with uv, need to look into this further * Github actions
|
This pull request sets up GitHub code scanning for this repository. Once the scans have completed and the checks have passed, the analysis results for this pull request branch will appear on this overview. Once you merge this pull request, the 'Security' tab will show more code scanning analysis results (for example, for the default branch). Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results. For more information about GitHub code scanning, check out the documentation. |
Welcome to Codecov 🎉Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests. Thanks for integrating Codecov - We've got you covered ☂️ |
| open-pull-requests-limit: 25 | ||
|
|
||
| # Python | ||
| - package-ecosystem: "pip" # See documentation for possible values |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can now use "uv" as the value for python repos with uv dependency management
https://docs.astral.sh/uv/guides/integration/dependency-bots/#dependabot
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, the issue linked in those docs says it doesn't update the pyproject.toml file yet, sounds like it's not quite ready for prime time
| export UV_PROJECT_ENVIRONMENT="${pythonLocation}" | ||
| uv sync --locked |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
uv can also manage your python installation if you have committed the .python-version file, so you don't need the python install step or the UV_PROJECT_ENVIRONMENT env var
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, but the current way means we can matrix test multiple versions of python if we want to, which I'm guessing we'll probably want to
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May want to remove the .python-version file in that case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure how that file interacts with the UV_PROJECT_ENVIRONMENT variable - it might cause the file to be ignored. I'd like to keep the file for when people are installing deps locally. It it becomes an issue we can delete it in the test.yml file
|
|
||
| ## Installation | ||
|
|
||
| TODO INSTALL setup a KBase pypi org and publish there |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please announce when you do this as there are a few other packages that it'd be great to put up there
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's the plan
| /.pytest_cache/ | ||
| __pycache__ | ||
| /.venv/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do you need to add in the directories that get created when you run uv build so you don't get the build gubbins committed to the repo?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't built a package with uv yet so I didn't know about the gubbins, but when I do I'll update the ignore
src/kbase/auth/client.py
Outdated
| from kbase.auth._async.client import AsyncClient as AsyncKBaseAuthClient # @UnusedImport | ||
| from kbase.auth._sync.client import Client as KBaseAuthClient # @UnusedImport No newline at end of file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what tool/IDE is the @UnusedImport annotation for? ruff uses unused-import or F401.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pydev
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know why these IDEs and linters can't get their acts together and decide on a shared syntax for disabling linting for a rule or a line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, that'd be nice
test/test_client.py
Outdated
| with pytest.raises(type(expected)) as e: | ||
| KBaseAuthClient.create(url) | ||
| assert str(e.value) == expected.args[0] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
with pytest.raises(type(expected), match="some string to match"):
KBaseAuthClient.create(url)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
test/test_client.py
Outdated
| @@ -0,0 +1,63 @@ | |||
| import pytest | |||
|
|
|||
| from conftest import AUTH_URL, AUTH_VERSION, auth_users # @UnusedImport | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why import something if it is not used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's used in the last 2 tests. PyDev doesn't realize it's a fixture since it just looks like an argument to a method
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PyDev is not making a strong case for its adoption ATM...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm actually curious at to how many tools would catch this. They'd have to know the specifics of pytest fixtures, and if the method isn't marked as a test they don't really have enough info to know that the function / method argument is a fixture
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any fixture in conftest is already discoverable in test files by pytest, so adding it to direct imports can cause weird behavior for any dev thing looking at it.
Does PyDev whine if it's not included as an import?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Huh, it does not. Well that's neat, I've been doing that for years now. I wonder how I got that in my head.
Anyway, removed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe I started putting the import there because otherwise it's not clear where the fixture comes from to the uninitiated
test/conftest.py
Outdated
| import time | ||
|
|
||
|
|
||
| # settings are coming from the docker-compose file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"settings are coming from" ==> either "settings come from" or "settings are from"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean all 3 are grammatically correct I think, but in the interests of brevity I've updated it
| status = j["Health"] | ||
| if status == "healthy": |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could just check the content of j["Health"] without saving it to a variable -- if j.get("Health") ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
status is used a few lines down in the method
| "pytest-cov==7.0.0", | ||
| "requests==2.32.5", | ||
| "unasync==0.6.0", | ||
| ] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would encourage good practices by adding in a code formatting/linting section with a ruff config here. Just makes life easier not to have to worry about code format and dumb errors that can be caught by tools.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not a big fan linters at this point, they're too opinionated and catch mostlyi trivial stuff
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I much prefer using a code formatter wherever possible -- if everyone's code is laid out the same, it's much quicker to read, review, and understand.
Linters catch all kinds of useful errors, especially if you have devs with different levels of experience working on a project. You or I might have the ability to produce perfect code every time, but that isn't true for everyone.
As for being opinionated, what's that expression about the pot and the kettle...? 😉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm definitely opinionated, but when it comes to code style I think I keep my opinions in check these days. As long as it's readable I don't care too much.
If we start having junior devs work on this repo I'm willing to revisit adding a code formatter, but right now the ROI just isn't there. I've spent too much time setting up, configuring, and integrating code formatters only to have them waste my time catching trailing whitespace (cough codacy cough). Right now there's little to no benefit IMO
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is extremely quick and easy to set up ruff to format your code -- it even looks as though PyDev has ruff as one of the formatters. I believe ruff catches trailing whitespace, and any decent IDE should also have a setting for it. You can also add a standard .editorconfig file to do that -- e.g. https://github.com/linkml/linkml/blob/main/.editorconfig
We have already spent more time debating this than it would have taken to add a section to the pyproject.toml file with the setup for formatting and linting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But not more time than it will take in the future having test runs fail due to trivial issues, fixing said trivial issues that don't need to be fixed, and tweaking the config until everyone's equally annoyed with it. I've been through this cycle several times before with many different linters; it's not necessary and not worth the effort
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have had the opposite experience.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, it seems we're at an impasse - can you agree to leave things as they are for now and add a linter when / if it becomes an issue?
src/kbase/auth/_async/client.py
Outdated
| j = r.json() | ||
| except Exception: | ||
| err = "Non-JSON response from KBase auth server, status code: " + str(r.status_code) | ||
| # TDOO TEST LOgging in the future |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
two typos in one line. The horror!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I may not survive the spiral of shame I'm in
Anyway, fixed
| """ | ||
| A client for the KBase Authentication service. | ||
| """ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add a line here that indicates that the sync client is derived from the asynchronous client, so that if people don't read the README, they won't go and waste a load of effort editing two files when they could just edit one?
Something neutral that could appear in either file, e.g. "The sync auth client is derived from the async client, and any edits should be made to src/kbase/auth/_async/client.py and propagated using the script process_unasync.py".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed, although I'm referencing the readme for instructions so there's a single point of truth
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You would (or wouldn't) be surprised how many people don't bother with the README but launch straight into the code. Not that I have ever done that, of course.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wouldn't be surprised in the least
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a README here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes? Not sure if this is a joke I'm missing or a serious question
src/kbase/auth/_async/client.py
Outdated
| raise | ||
| # TODO CLIENT look through the myriad of auth clients to see what functionality we need | ||
| # TODO CLIENT cache token & user using cachefor value from token | ||
| # TODO RELIABILIY could add retries for these methods, tenacity looks useful |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO spelling of RELIABILITY
TODO add a space in "token & user using cachefor value"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed reliability
cachefor is actually the field in the token data structure, so I'm leaving that
|
|
||
| import httpx | ||
| import logging | ||
| from typing import Self |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
where is this used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's the return type of the create() classmethod
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, seen it - thanks!
src/kbase/auth/_async/client.py
Outdated
|
|
||
| def _check_request(r: httpx.Request): | ||
| try: | ||
| j = r.json() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know that characters are at a premium right now, but could you spare a few more for these variable names? j is rather terse and uninformative.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks to you my keyboard is now running out of charact
| # TODO TEST add a way to keep things running and be able to rerun tests | ||
| _run_dc(env, "down") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternatively, have a wrapper script that does all the docker set up stuff, executes pytest, and then shuts everything down. That would enable you to keep your other docker containers running whilst working on the python container.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's basically what I would make this do - just leave it up after running tests the first time, and then shut it down with docker compose down
|
FYI - for some reason GitHub will not let me resolve conversations, so I have not been able to achieve closure on some of these points. |
... which is what it's actually doing
|
Changed the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a minor change along with @ialarmedalien 's comments..
| """ | ||
| A client for the KBase Authentication service. | ||
| """ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a README here?
test/test_client.py
Outdated
| @@ -0,0 +1,63 @@ | |||
| import pytest | |||
|
|
|||
| from conftest import AUTH_URL, AUTH_VERSION, auth_users # @UnusedImport | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any fixture in conftest is already discoverable in test files by pytest, so adding it to direct imports can cause weird behavior for any dev thing looking at it.
Does PyDev whine if it's not included as an import?
| await AsyncKBaseAuthClient.create(url) | ||
|
|
||
|
|
||
| @pytest.mark.asyncio |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can add a block like:
[tool.pytest.ini_options]
asyncio_mode = "auto"
to pyproject.toml and not have to put the annotation on async tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually prefer the explicit marking of tests
|
@ialarmedalien approved out of band |
Adds