{"id":29053474,"url":"https://github.com/celeo/preston","last_synced_at":"2025-10-08T23:59:09.500Z","repository":{"id":51003323,"uuid":"62006808","full_name":"Celeo/Preston","owner":"Celeo","description":"Python 3 library for accessing EVE Online's ESI API","archived":false,"fork":false,"pushed_at":"2025-06-10T17:28:56.000Z","size":284,"stargazers_count":31,"open_issues_count":2,"forks_count":17,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-09-21T21:05:11.554Z","etag":null,"topics":["esi","eve-online","python"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Celeo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2016-06-26T20:22:21.000Z","updated_at":"2025-09-09T20:31:55.000Z","dependencies_parsed_at":"2025-07-24T20:37:49.389Z","dependency_job_id":"ea1c0e61-75f5-4157-8856-901c09299dcf","html_url":"https://github.com/Celeo/Preston","commit_stats":{"total_commits":113,"total_committers":8,"mean_commits":14.125,"dds":0.4336283185840708,"last_synced_commit":"0e09879eb2f225e4bc8c47d4ccb4b4595d67a181"},"previous_names":["celeo/prest"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Celeo/Preston","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Celeo%2FPreston","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Celeo%2FPreston/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Celeo%2FPreston/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Celeo%2FPreston/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Celeo","download_url":"https://codeload.github.com/Celeo/Preston/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Celeo%2FPreston/sbom","scorecard":{"id":27946,"data":{"date":"2025-08-11","repo":{"name":"github.com/Celeo/Preston","commit":"e8ec25dd9843e882fdad57a015bd7fd5d2ef3964"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.9,"checks":[{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":10,"reason":"23 commit(s) and 2 issue activity found in the last 90 days -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/ci.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Code-Review","score":1,"reason":"Found 2/14 approved changesets -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:13: update your workflow using https://app.stepsecurity.io/secureworkflow/Celeo/Preston/ci.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/ci.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/Celeo/Preston/ci.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/ci.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/Celeo/Preston/ci.yml/master?enable=pin","Info:   0 out of   1 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   2 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 18 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-14T18:21:49.830Z","repository_id":51003323,"created_at":"2025-08-14T18:21:49.830Z","updated_at":"2025-08-14T18:21:49.830Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279000705,"owners_count":26082862,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-08T02:00:06.501Z","response_time":56,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["esi","eve-online","python"],"created_at":"2025-06-27T01:06:29.336Z","updated_at":"2025-10-08T23:59:09.462Z","avatar_url":"https://github.com/Celeo.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Preston\n\n[![CI](https://github.com/Celeo/Preston/actions/workflows/ci.yml/badge.svg)](https://github.com/Celeo/Preston/actions/workflows/ci.yml)\n[![codecov](https://codecov.io/gh/Celeo/preston/branch/master/graph/badge.svg?token=2R9RY3P229)](https://codecov.io/gh/Celeo/preston)\n[![Python version](https://img.shields.io/badge/Python-3.11+-blue)](https://www.python.org/)\n[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)\n[![License](https://img.shields.io/badge/License-MIT-green)](LICENSE)\n\nPreston is a Python library for accessing EVE Online's ESI API.\n\n## Quick links\n\n* EVE ESI: \u003chttps://esi.evetech.net\u003e\n* EVE developers: \u003chttps://developers.eveonline.com\u003e\n\n## Installation\n\nFrom [pip](https://pip.pypa.io/en/stable/):\n\n```sh\npip install preston\n```\n\n## Initialization\n\n```python\nfrom preston import Preston\n\npreston = Preston()\n```\n\nThere are values that you can pass to `__init__` as kwargs; those are detailed in the docstring for the class.\n\n## Usage\n\nThere are 3 main things that Preston does:\n\n1. Unauthenticated calls to ESI\n2. User authentication\n3. Authenticated calls to ESI for that user\n\nFor #1, all you need to do is initialize a Preston object and make the call:\n\n```python\npreston = Preston(\n    user_agent='some_user_agent'\n)\n\ndata = preston.get_op('get_characters_character_id', character_id=91316135)\n# ...\n```\n\nYou should always include a good `user_agent`.\n\nAdditionally, a `post_op` method exists, that takes a dictionary (instead of **kwargs) and another parameter; the former is used like above, to satisfy the URL parameters, and the latter is sent to the ESI endpoint as the payload.\n\nFor #2, there are 2 methods that you'll need, `get_authorize_url` and `authenticate`, and several `__init__` kwargs.\n\n```python\npreston = Preston(\n    user_agent='some_user_agent',\n    client_id='something',\n    client_secret='something',\n    callback_url='something',\n    scope='maybe_something',\n)\n```\n\nYou can find the last 4 values in your application on the [EVE Dev site](https://developers.eveonline.com/).\n\nWhen you have a Preston instance constructed like this, you can make the call to `get_authorize_url`:\n\n```python\npreston.get_authorize_url()\n# https://login.eveonline.com/oauth/...\n```\n\nThis is the URL that your user needs to visit and complete the flow. They'll be redirected to your app's callback URL, so you have to be monitoring that.\n\n\u003e [!NOTE]\n\u003e `get_authorize_url()` takes an optional argument 'state' that you can use to make sure you get the code back from the right user. \n\u003e If you do not set this then the url includes `?state=default`\n\nWhen you get their callback, take the code paramter from the URL and pass it to `authenticate`:\n\n```python\nauth = preston.authenticate('their_code_here')\n```\n\n\u003e [!NOTE]\n\u003e The return variable and it's reassignment: this method returns a *new* instance, with the corresponding variables and headers setup for authenticated ESI calls.\n\nFinally for #3, having followed the steps above, you just make calls like previously, but you can do so to the authenticated-only endpoints. Make sure that if you're calling\nan endpoint that requires a specific scope, your app on EVE Devs has that scoped added and you've supplied it to the Preston initialization.\n\n### Resuming authentication\n\nIf your app uses scopes, it'll receive a `refresh_token` alongside the `access_token`. The access token, per usual, only lasts 20 minutes before it expires. In this situation,\nthe refresh token can be used to get a *new* access token and a *new* refresh token. If your Preston instance has a refresh token, this will be done automatically when the access token expires.\n\nIf you want to store your current refresh token so you can fetch data with authorization later on again, you can do that with preston as following.\n\nTo get the current refresh token and keep getting informed when it changes, initialize Preston with a refresh token callback function.\nThis function should look something like this:\n\n```python\ndef my_refresh_token_callback(preston: Preston):\n    # To figure out which user called the callback, you can use the whoami() function.\n    current_user_info = preston.whoami()\n    character_id = current_user_info.get(\"character_id\")\n    scopes = current_user_info.get(\"scopes\")\n    \n    # Now you probably want store the refresh token somewhere e.g. your db\n    # Or something else persistent. In the simple case all users have the same scopes\n    # and you are using a python shelve, you would do something like this:\n    python_shelve[character_id] = preston.refresh_token\n```\n\nYou can pass this function to Preston on initialization:\n\n```python\npreston = Preston(\n    ...,\n    refresh_token_callback=my_refresh_token_callback\n)\n```\n\nNow every time Preston needs to get new tokens, it will also call your function so you can stay up to date.\n\nIf you want to resume authentication, you can use the refresh token on initialization:\n\n```python\npreston = Preston(\n    ...,\n    refresh_token='your_refresh_token_here'\n)\n```\nOr you can get an authorized Preston instance from an unauthorized:\n\n```python\nauth = preston.authenticate_from_token('your_refresh_token_here')\n```\n\n\u003e [!NOTE]\n\u003e You can also pass the `access_token` to a new Preston instance, but there's less of a use case for that, as either you have an app with scopes, yielding a refresh token,\n\u003e or an authentication-only app where you only use the access token to verify identity and some basic information before moving on.\n\n## Error Handling\n\nPreston usually retries network-related exceptions up to 4 times with exponential backoff (1, 2, 4, 8, ... seconds), and times out any single request\nafter 6 seconds. This means that at most any single call can take 39 seconds. You can modify this behaviour with optional arguments:\n```python\npreston = Preston(\n    timeout=\"timeout_per_request_in_seconds\",\n    retrues=\"max_number_of_retries\",\n)\n```\n\nFor non network related issues, Preston raises the Exceptions generated by requests immediately. \nYou can find all possible errors [here](https://requests.readthedocs.io/en/latest/_modules/requests/exceptions/).\n\nA simple error handling scheme might look something like this:\n\n```python\ntry:\n    authed_preston = preston.authenticate_from_refresh(character.token)\nexcept HTTPError as exp:\n    # Some kind of error the user has to deal with e.g. no permissions, token revoked on website\n    ...\nexcept ConnectionError as exp:\n    # Network issue. If it is a background task probably fine to log and skip, if there is a user tell him network is bad\n    ...\n    # in both cases The exception contains the full response e.g. you can\n    print(exp.response.status_code)\n    print(exp.response.text)\n    # etc.\n```\n\n## Developing\n\n### Building\n\n### Requirements\n\n* Git\n* uv\n* Python 3.8+\n\n### Steps\n\n```sh\ngit clone https://github.com/Celeo/preston\ncd preston\nuv sync\n```\n\n### Running tests\n\n| | |\n| --- | --- |\n| No coverage | `uv run pytest`\n| Coverage printout | `uv run pytest --cov=preston` |\n| Coverage report | `uv run pytest --cov=preston --cov-report=html` |\n\n## License\n\nLicensed under MIT ([LICENSE](LICENSE)).\n\n## Contributing\n\nPRs are welcome. Please follow PEP8 (I'm lenient on E501) and use [Google-style docstrings](https://www.sphinx-doc.org/en/master/usage/extensions/example_google.html#example-google).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fceleo%2Fpreston","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fceleo%2Fpreston","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fceleo%2Fpreston/lists"}