{"id":31539777,"url":"https://github.com/tell-k/uadetector","last_synced_at":"2025-10-04T09:14:46.935Z","repository":{"id":62585992,"uuid":"111078334","full_name":"tell-k/uadetector","owner":"tell-k","description":"WSGI Middleware and web framework extensions for handling User-Agent.","archived":false,"fork":false,"pushed_at":"2018-02-20T11:13:52.000Z","size":47,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-09-25T13:47:28.925Z","etag":null,"topics":["django","flask","pyramid","python","tornado","useragent","useragent-parser","wsgi-middleware"],"latest_commit_sha":null,"homepage":"https://pypi.python.org/pypi/uadetector","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/tell-k.png","metadata":{"files":{"readme":"README.rst","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-11-17T08:31:18.000Z","updated_at":"2018-09-10T15:59:37.000Z","dependencies_parsed_at":"2022-11-03T22:04:49.096Z","dependency_job_id":null,"html_url":"https://github.com/tell-k/uadetector","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/tell-k/uadetector","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tell-k%2Fuadetector","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tell-k%2Fuadetector/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tell-k%2Fuadetector/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tell-k%2Fuadetector/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tell-k","download_url":"https://codeload.github.com/tell-k/uadetector/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tell-k%2Fuadetector/sbom","scorecard":{"id":873284,"data":{"date":"2025-08-11","repo":{"name":"github.com/tell-k/uadetector","commit":"62584b9f65858b482bb5959718906445dcdd472a"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/22 approved changesets -- score normalized to 0","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":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"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":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"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":"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":"Dangerous-Workflow","score":-1,"reason":"no workflows found","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":"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":"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":"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":"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":"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":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"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 3 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-24T04:58:05.628Z","repository_id":62585992,"created_at":"2025-08-24T04:58:05.628Z","updated_at":"2025-08-24T04:58:05.628Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278290975,"owners_count":25962664,"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-04T02:00:05.491Z","response_time":63,"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":["django","flask","pyramid","python","tornado","useragent","useragent-parser","wsgi-middleware"],"created_at":"2025-10-04T09:14:45.060Z","updated_at":"2025-10-04T09:14:46.921Z","avatar_url":"https://github.com/tell-k.png","language":"Python","readme":"WSGI Middleware and web framework extensions for handling User-Agent. Thanks to `woothee \u003chttps://github.com/woothee/woothee-python\u003e`_ , UADetector supports various User-Agents. This library respects to `k0kubun/rack-user_agent \u003chttps://github.com/k0kubun/rack-user_agent\u003e`_ .\n\n|travis| |coveralls| |version| |license|\n\nInstallation\n===================\n\n::\n\n $ pip install uadetector\n\n\nUsage\n=====================\n\nWSGI middleware\n----------------------\n\nThis middleware provides a ``uadetector.useragent.UserAgent`` object to handling User-agents. \n\n.. code-block:: python\n\n from wsgiref.simple_server import make_server\n\n # import middleware\n from uadetector import UADetector\n\n def app(environ, start_response):\n     start_response('200 OK', [('Content-Type', 'text/plain')])\n\n     # get 'UserAgent' object from environ dict.\n     ua = environ.get('uadetector.useragent')\n\n     ua.user_agent       #=\u003e \"Mozilla/5.0 (Macintosh; ...\"\n     ua.device_type      #=\u003e \"pc\"\n     ua.os               #=\u003e \"Mac OSX\"\n     ua.browser          #=\u003e \"Chrome\"\n     ua.from_pc          #=\u003e True\n     ua.from_smartphone  #=\u003e False\n\n     return [ua.os.encode('utf-8')]\n\n # Apply middleware\n application = UADetector(app)\n\n if __name__ == \"__main__\":\n     with make_server('127.0.0.1', 8000, application) as server:\n         print(\"Serving on port 8000...\")\n         server.serve_forever()\n\nYou can also replace the key of ``environ`` or the ``UserAgent`` class.\n\n.. code-block:: python\n\n from uadetector.useragent import UserAgent\n\n class MyUserAgent(UserAgent):\n      # Write your custom codes.\n\n # Apply middleware\n application = UADetector(\n    app,\n    envorion_key='your.favorite.key'\n    useragent_class='path.to.MyUserAgent'\n )\n\nSee also `WSGI example \u003chttps://github.com/tell-k/uadetector/blob/master/examples/wsgi/\u003e`_.\n\nWeb framework extensions\n--------------------------------\n\nSome web frameworks provide a way to extend in a different way from WSGI Middleware. This library provide shortcuts according to that way.\n\n**Caution: I do not actively support individual frameworks. If you are worried, you should use WSGIMiddleware.**\n\nDjango\n~~~~~~~~~\n\nYou can use Django's ``MIDDLEWARE``.\n\n.. code-block:: python\n\n # settings.py\n\n MIDDLEWARE = [\n    # Add UADetecorMiddleware\n    'uadetector.django.middleware.UADetectorMiddleware',\n    # ... omit ...\n ]\n\n.. code-block:: python\n\n # views.py\n\n def index_view(request):\n     print(request.ua.from_smartphone) # =\u003e True or False\n     # ... omit ...\n\nCustomize property name of request object and replace UserAgent class.\n\n.. code-block:: python\n\n # settings.py\n\n UADETECTOR_REQUEST_PROPERTY_NAME = 'agent' # =\u003e You can use \"request.agent\"\n UADETECTOR_USERAGENT_CLASS = 'path.to.MyUserAgent'\n\nSee also `Dajngo example \u003chttps://github.com/tell-k/uadetector/blob/master/examples/django/\u003e`_.\n\nTips: `Switch templates based on User-Agent \u003chttps://github.com/tell-k/django-variantmpl#switch-templates-based-on-user-agent\u003e`_  (using `django-variantmpl \u003chttps://github.com/tell-k/django-variantmpl\u003e`_ ).\n\nPyramid\n~~~~~~~~~\n\nYou can use ``config.add_request_method``.\n\n.. code-block:: python\n\n from uadetector.pyramid import ua_prop\n\n\n def index(request):\n     print(request.ua.from_smartphone) # =\u003e True or False\n     # ... omit ...\n\n\n with Configurator() as config:\n     config.add_route('index', '/')\n     config.add_view(index, route_name='index')\n\n     config.add_request_method(ua_prop(), name='ua', reify=True)\n     # ... omit ...\n\nCustomize property name of request object and replace UserAgent class.\n\n.. code-block:: python\n\n config.add_request_method(\n     ua_prop('path.to.MyUserAgent'),\n     name='agent',  # =\u003e You can use \"request.agent\"\n     reify=True\n )\n\nSee also `Pyramid example \u003chttps://github.com/tell-k/uadetector/blob/master/examples/pyramid/\u003e`_.\n\nFlask\n~~~~~~~~~\n\nYou can use ``Flask Extension``.\n\n.. code-block:: python\n\n from flask import Flask, request\n from uadetector.flask import UADetector\n\n app = Flask(__name__)\n UADetector(app)\n\n @app.route('/')\n def index():\n     print(request.ua.from_smartphone) # =\u003e True or False\n     # ... omit ...\n\nCustomize property name of request object and replace UserAgent class.\n\n.. code-block:: python\n\n app = Flask(__name__)\n\n app.config['UADETECTOR_USERAGENT_CLASS'] = 'path.to.MyUserAgent'\n app.config['UADETECTOR_REQUEST_PROPERTY_NAME'] = 'agent' # =\u003e You can use \"request.agent\"\n\n UADetector(app)\n\nSee also `Flask example \u003chttps://github.com/tell-k/uadetector/blob/master/examples/flask/\u003e`_.\n\nTornado\n~~~~~~~~~\n\nYou can use custom ``RequestHandler``.\n\n.. code-block:: python\n\n  from uadetector.tornado.web import RequestHandler\n\n  class IndexHandler(RequestHandler):\n\n      def get(self):\n          print(self.request.ua.from_smartphone) # =\u003e True or False\n          # ... omit ...\n\nCustomize property name of request object and replace UserAgent class.\n\n.. code-block:: python\n\n from tornado.options import define\n from uadetector.tornado.web import RequestHandler\n\n define(\n     'uadetector_request_property_name',\n     default='agent', # =\u003e You can use \"self.request.agent\"\n )\n define(\n     'uadetector_useragent_class',\n     default='path.to.MyUserAgent'\n )\n\n class IndexHandler(RequestHandler):\n\nSee also `Tornado example \u003chttps://github.com/tell-k/uadetector/blob/master/examples/tornado/\u003e`_.\n\nUserAgent\n===================\n\nList of properties of ``uadetector.useragent.UserAgent`` object.\n\nattrs\n-----------\n\n* UserAgent.device_variant\n* UserAgent.device_type\n* UserAgent.os\n* UserAgent.os_version\n* UserAgent.browser\n* UserAgent.browser_version\n* UserAgent.browser_vendor\n\nhelpers\n-----------\n\n* UserAgent.from_pc\n* UserAgent.from_smartphone\n* UserAgent.from_mobilephone\n* UserAgent.from_appliance\n* UserAgent.from_crawler\n\ndetectors\n-----------\n\n* UserAgent.smartphone_version\n* UserAgent.from_iphone\n* UserAgent.from_ipad\n* UserAgent.from_ipod\n* UserAgent.from_android\n* UserAgent.from_android_tablet\n* UserAgent.from_windows_phone\n* UserAgent.from_ios\n* UserAgent.from_android_os\n\nTips\n===================\n\nIf you want a ``UserAgent`` object simply from the User-Agent string, Please use ``get_useruseragent``.\n\n.. code-block:: python\n\n from uadetector import get_useragent\n\n ua_string = \"Mozilla/5.0 (iPhone; CPU iPhone OS ...\"\n\n ua = get_useragent(ua_string)\n us.from_smartphone # =\u003e True\n\n # Use custom useragent class\n ua = get_useragent(ua_string, useragent_class='path.to.MyUserAgent')\n\nSupport\n========\n\nSupport latest 3 minor versions.\n\n* Python 3.4, 3.5, 3.6\n* Django 1.10, 1.11, 2.0\n* Pyramid 1.7, 1.8, 1.9\n* Flask 0.10, 0.11, 0.12\n* Tornado 4.5, 4.6, 4.7\n\nLicense\n========\n\nMIT License\n\nAuthors\n=======\n\n* tell-k \u003cffk2005 at gmail.com\u003e\n\nHistory\n=======\n\n0.1.3(Feb 20, 2018)\n---------------------\n\n* `Lazy parsing User-Agent string \u003chttps://github.com/tell-k/uadetector/pull/1\u003e`_ .\n\n0.1.2(Feb 19, 2018)\n---------------------\n\n* First release\n\n\n.. |travis| image:: https://travis-ci.org/tell-k/uadetector.svg?branch=master\n    :target: https://travis-ci.org/tell-k/uadetector\n\n.. |coveralls| image:: https://coveralls.io/repos/tell-k/uadetector/badge.png\n    :target: https://coveralls.io/r/tell-k/uadetector\n    :alt: coveralls.io\n\n.. |version| image:: https://img.shields.io/pypi/v/uadetector.svg\n    :target: http://pypi.python.org/pypi/uadetector/\n    :alt: latest version\n\n.. |license| image:: https://img.shields.io/pypi/l/uadetector.svg\n    :target: http://pypi.python.org/pypi/uadetector/\n    :alt: license\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftell-k%2Fuadetector","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftell-k%2Fuadetector","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftell-k%2Fuadetector/lists"}