[
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n- package-ecosystem: pip\n  directory: \"/\"\n  schedule:\n    interval: daily\n  open-pull-requests-limit: 10\n- package-ecosystem: github-actions\n  directory: /\n  schedule:\n    interval: daily\n  open-pull-requests-limit: 10\n"
  },
  {
    "path": ".github/workflows/auto-merge.yml",
    "content": "name: Dependabot auto-merge\non: pull_request_target\n\npermissions:\n  pull-requests: write\n  contents: write\n\njobs:\n  dependabot:\n    runs-on: ubuntu-latest\n    if: ${{ github.actor == 'dependabot[bot]' }}\n    steps:\n      - name: Dependabot metadata\n        id: metadata\n        uses: dependabot/fetch-metadata@v3.1.0\n        with:\n          github-token: \"${{ secrets.GITHUB_TOKEN }}\"\n      - name: Enable auto-merge for Dependabot PRs\n        run: gh pr merge --auto --squash \"$PR_URL\"\n        env:\n          PR_URL: ${{github.event.pull_request.html_url}}\n          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}\n"
  },
  {
    "path": ".github/workflows/ci-cd.yml",
    "content": "name: CI\n\non:\n  push:\n    branches:\n      - master\n      - '[0-9].[0-9]+'  # matches to backport branches, e.g. 3.6\n    tags: [ 'v*' ]\n  pull_request:\n    branches:\n      - master\n      - '[0-9].[0-9]+'\n\n\njobs:\n  lint:\n    name: Linter\n    runs-on: ubuntu-latest\n    timeout-minutes: 5\n    steps:\n    - name: Checkout\n      uses: actions/checkout@v6\n    - name: Setup Python\n      uses: actions/setup-python@v6\n      with:\n        python-version: 3.11\n        cache: 'pip'\n        cache-dependency-path: '**/requirements*.txt'\n    - name: Pre-Commit hooks\n      uses: pre-commit/action@v3.0.1\n    - name: Install dependencies\n      uses: py-actions/py-dependency-install@v4\n      with:\n        path: requirements-dev.txt\n    - name: Install itself\n      run: |\n        pip install .\n    - name: Run linter\n      run: |\n        make lint\n    - name: Prepare twine checker\n      run: |\n        pip install -U build twine wheel\n        python -m build\n    - name: Run twine checker\n      run: |\n        twine check dist/*\n\n  test:\n    name: Test\n    strategy:\n      matrix:\n        pyver: ['3.9', '3.10', '3.11', '3.12', '3.13']\n    runs-on: ubuntu-latest\n    timeout-minutes: 15\n    steps:\n    - name: Checkout\n      uses: actions/checkout@v6\n    - name: Setup Python ${{ matrix.pyver }}\n      uses: actions/setup-python@v6\n      with:\n        python-version: ${{ matrix.pyver }}\n        cache: 'pip'\n        cache-dependency-path: '**/requirements*.txt'\n    - name: Install dependencies\n      uses: py-actions/py-dependency-install@v4\n      with:\n        path: requirements-dev.txt\n    - name: Run unittests\n      run: pytest tests\n      env:\n        COLOR: 'yes'\n    # - run: python -m coverage xml\n    # - name: Upload coverage\n    #   uses: codecov/codecov-action@v5\n    #   with:\n    #     fail_ci_if_error: true\n    #     token: ${{ secrets.CODECOV_TOKEN }}\n\n  check:  # This job does nothing and is only used for the branch protection\n    if: always()\n\n    needs: [lint, test]\n\n    runs-on: ubuntu-latest\n\n    steps:\n    - name: Decide whether the needed jobs succeeded or failed\n      uses: re-actors/alls-green@release/v1\n      with:\n        jobs: ${{ toJSON(needs) }}\n\n  deploy:\n    name: Deploy\n    runs-on: ubuntu-latest\n    needs: [check]\n    if: github.event_name == 'push' && contains(github.ref, 'refs/tags/')\n    permissions:\n      contents: write  # IMPORTANT: mandatory for making GitHub Releases\n      id-token: write  # IMPORTANT: mandatory for trusted publishing & sigstore\n    environment:\n      name: pypi\n      url: >-\n        https://pypi.org/project/${{ env.PROJECT_NAME }}/${{ github.ref_name }}\n    steps:\n    - name: Checkout\n      uses: actions/checkout@v6\n    - name: Setup Python\n      uses: actions/setup-python@v6\n      with:\n        python-version: 3.9\n    - name: Install dependencies\n      run:\n        python -m pip install -U pip wheel setuptools build twine\n    - name: Build dists\n      run: |\n        python -m build\n    - name: >-\n        Publish 🐍📦 to PyPI\n      uses: pypa/gh-action-pypi-publish@release/v1\n    - name: Sign the dists with Sigstore\n      uses: sigstore/gh-action-sigstore-python@v3.3.0\n      with:\n        inputs: >-\n          ./dist/${{ env.PROJECT_NAME }}*.tar.gz\n          ./dist/${{ env.PROJECT_NAME }}*.whl\n    - name: Upload artifact signatures to GitHub Release\n      # Confusingly, this action also supports updating releases, not\n      # just creating them. This is what we want here, since we've manually\n      # created the release above.\n      uses: softprops/action-gh-release@v3\n      with:\n        # dist/ contains the built packages, which smoketest-artifacts/\n        # contains the signatures and certificates.\n        files: dist/**\n    # - name: Make Release\n    #   uses: aio-libs/create-release@v1.6.6\n    #   with:\n    #     changes_file: CHANGES.rst\n    #     name: aiohttp-jinja2\n    #     version_file: aiohttp_jinja2/__init__.py\n    #     github_token: ${{ secrets.GITHUB_TOKEN }}\n    #     pypi_token: ${{ secrets.PYPI_API_TOKEN }}\n    #     dist_dir: dist\n    #     fix_issue_regex: \"`#(\\\\d+) <https://github.com/aio-libs/aiohttp-jinja2/issues/\\\\1>`\"\n    #     fix_issue_repl: \"(#\\\\1)\"\n"
  },
  {
    "path": ".github/workflows/codeql.yml",
    "content": "name: \"CodeQL\"\n\non:\n  push:\n    branches: [ \"master\" ]\n  pull_request:\n    branches: [ \"master\" ]\n  schedule:\n    - cron: \"43 0 * * 4\"\n\njobs:\n  analyze:\n    name: Analyze\n    runs-on: ubuntu-latest\n    permissions:\n      actions: read\n      contents: read\n      security-events: write\n\n    strategy:\n      fail-fast: false\n      matrix:\n        language: [ javascript, python ]\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n\n      - name: Initialize CodeQL\n        uses: github/codeql-action/init@v4\n        with:\n          languages: ${{ matrix.language }}\n          queries: +security-and-quality\n\n      - name: Autobuild\n        uses: github/codeql-action/autobuild@v4\n        if: ${{ matrix.language == 'javascript' || matrix.language == 'python' }}\n\n      - name: Perform CodeQL Analysis\n        uses: github/codeql-action/analyze@v4\n        with:\n          category: \"/language:${{ matrix.language }}\"\n"
  },
  {
    "path": ".gitignore",
    "content": "#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n# Byte-compiled / optimized / DLL files\n# C extensions\n# Distribution / packaging\n# Installer logs\n# PyBuilder\n# PyInstaller\n# Sphinx documentation\n# Unit test / coverage reports\n*,cover\n*.egg-info/\n*.manifest\n*.py[cod]\n*.so\n*.spec\n.Python\n.coverage\n.coverage.*\n.eggs/\n.installed.cfg\n.pytest_cache\n/*.egg\n/.cache\n/.tox/\n/build/\n/dist/\n/env/\n__pycache__/\ncoverage.xml\ndevelop-eggs/\ndocs/_build/\ndownloads/\neggs/\ngeckodriver.log\nhtmlcov/\nlib/\nlib64/\nnosetests.xml\nparts/\npip-delete-this-directory.txt\npip-log.txt\nsdist/\ntarget/\nvar/\n.python-version\n"
  },
  {
    "path": ".jscs.json",
    "content": "{\n    \"requireCurlyBraces\": [\n        \"if\",\n        \"else\",\n        \"for\",\n        \"while\",\n        \"do\",\n        \"try\",\n        \"catch\"\n    ],\n    \"requireOperatorBeforeLineBreak\": true,\n    \"requireCamelCaseOrUpperCaseIdentifiers\": true,\n    \"maximumLineLength\": {\n      \"value\": 80,\n      \"allowComments\": true,\n      \"allowRegex\": true\n    },\n    \"validateIndentation\": 2,\n    \"validateQuoteMarks\": \"'\",\n\n    \"disallowMultipleLineStrings\": true,\n    \"disallowMixedSpacesAndTabs\": true,\n    \"disallowTrailingWhitespace\": true,\n    \"disallowSpaceAfterPrefixUnaryOperators\": true,\n    \"disallowMultipleVarDecl\": true,\n\n    \"requireSpaceAfterKeywords\": [\n      \"if\",\n      \"else\",\n      \"for\",\n      \"while\",\n      \"do\",\n      \"switch\",\n      \"return\",\n      \"try\",\n      \"catch\"\n    ],\n    \"requireSpaceBeforeBinaryOperators\": [\n        \"=\", \"+=\", \"-=\", \"*=\", \"/=\", \"%=\", \"<<=\", \">>=\", \">>>=\",\n        \"&=\", \"|=\", \"^=\", \"+=\",\n\n        \"+\", \"-\", \"*\", \"/\", \"%\", \"<<\", \">>\", \">>>\", \"&\",\n        \"|\", \"^\", \"&&\", \"||\", \"===\", \"==\", \">=\",\n        \"<=\", \"<\", \">\", \"!=\", \"!==\"\n    ],\n    \"requireSpaceAfterBinaryOperators\": true,\n    \"requireSpacesInConditionalExpression\": true,\n    \"requireSpaceBeforeBlockStatements\": true,\n    \"requireSpacesInForStatement\": true,\n    \"requireLineFeedAtFileEnd\": true,\n    \"requireSpacesInFunctionExpression\": {\n        \"beforeOpeningCurlyBrace\": true\n    },\n    \"disallowSpacesInsideObjectBrackets\": \"all\",\n    \"disallowSpacesInsideArrayBrackets\": \"all\",\n    \"disallowSpacesInsideParentheses\": true,\n\n    \"disallowMultipleLineBreaks\": true,\n    \"disallowNewlineBeforeBlockStatements\": [\n      \"if\", \"else\", \"try\", \"catch\", \"finally\", \"do\", \"while\", \"for\", \"function\"\n    ]\n}\n"
  },
  {
    "path": ".jshintrc",
    "content": "{\n\t\"asi\": false,\n\t\"bitwise\": false,\n\t\"boss\": false,\n\t\"browser\": true,\n\t\"camelcase\": true,\n\t\"couch\": false,\n\t\"curly\": true,\n\t\"debug\": false,\n\t\"devel\": true,\n\t\"dojo\": false,\n\t\"eqeqeq\": true,\n\t\"eqnull\": true,\n\t\"es3\": true,\n\t\"evil\": false,\n\t\"expr\": true,\n\t\"forin\": false,\n\t\"funcscope\": true,\n\t\"globalstrict\": false,\n\t\"immed\": true,\n\t\"iterator\": false,\n\t\"jquery\": false,\n\t\"lastsemic\": false,\n\t\"latedef\": false,\n\t\"laxbreak\": true,\n\t\"laxcomma\": false,\n\t\"loopfunc\": true,\n\t\"mootools\": false,\n\t\"multistr\": false,\n\t\"newcap\": true,\n\t\"noarg\": true,\n\t\"node\": false,\n\t\"noempty\": false,\n\t\"nonew\": true,\n\t\"nonstandard\": false,\n\t\"nomen\": false,\n\t\"onecase\": false,\n\t\"onevar\": false,\n\t\"passfail\": false,\n\t\"plusplus\": false,\n\t\"proto\": false,\n\t\"prototypejs\": false,\n\t\"regexdash\": true,\n\t\"regexp\": false,\n\t\"rhino\": false,\n\t\"undef\": true,\n\t\"unused\": \"strict\",\n\t\"scripturl\": true,\n\t\"shadow\": false,\n\t\"smarttabs\": true,\n\t\"strict\": false,\n\t\"sub\": false,\n\t\"supernew\": false,\n\t\"trailing\": true,\n\t\"validthis\": true,\n\t\"withstmt\": false,\n\t\"white\": true,\n\t\"worker\": false,\n\t\"wsh\": false,\n\t\"yui\": false,\n\t\"indent\": 4,\n\t\"predef\": [\"require\", \"define\", \"JSON\"],\n\t\"quotmark\": \"single\",\n\t\"maxcomplexity\": 10,\n\t\"esnext\": true\n}\n"
  },
  {
    "path": ".pep8rc",
    "content": "; TODO: This configuration currently not used in pytest-pep8, see\n; <https://bitbucket.org/pytest-dev/pytest-pep8/issues/11/add-option-to-use-pep8rc-configuration>\n\n[pep8]\nshow-source = yes\nstatistics = yes\ncount = yes\n"
  },
  {
    "path": ".pre-commit-config.yaml",
    "content": "repos:\n- repo: https://github.com/pre-commit/pre-commit-hooks\n  rev: 'v5.0.0'\n  hooks:\n  - id: check-merge-conflict\n    exclude: \"rst$\"\n- repo: https://github.com/asottile/yesqa\n  rev: v1.5.0\n  hooks:\n  - id: yesqa\n- repo: https://github.com/PyCQA/isort\n  rev: '6.0.1'\n  hooks:\n  - id: isort\n- repo: https://github.com/psf/black\n  rev: '25.1.0'\n  hooks:\n  - id: black\n    language_version: python3\n- repo: https://github.com/pre-commit/pre-commit-hooks\n  rev: 'v5.0.0'\n  hooks:\n  - id: check-case-conflict\n  - id: check-json\n  - id: check-xml\n  - id: check-yaml\n  - id: debug-statements\n  - id: check-added-large-files\n  - id: end-of-file-fixer\n    exclude: \"[.]md$\"\n  - id: requirements-txt-fixer\n  - id: trailing-whitespace\n    exclude: \"[.]ref$\"\n  - id: check-symlinks\n  - id: debug-statements\n- repo: https://github.com/asottile/pyupgrade\n  rev: 'v3.19.1'\n  hooks:\n  - id: pyupgrade\n    args: ['--py39-plus']\n- repo: https://github.com/PyCQA/flake8\n  rev: '7.1.2'\n  hooks:\n  - id: flake8\n- repo: https://github.com/rhysd/actionlint\n  rev: v1.7.7\n  hooks:\n  - id: actionlint-docker\n    args:\n    - -ignore\n    - 'SC2155:'\n    - -ignore\n    - 'SC2086:'\n    - -ignore\n    - 'SC1004:'\nci:\n  skip:\n  - actionlint-docker\n"
  },
  {
    "path": ".pylintrc",
    "content": "[MASTER]\n\n# Specify a configuration file.\n#rcfile=\n\n# Python code to execute, usually for sys.path manipulation such as\n# pygtk.require().\n#init-hook=\n\n# Profiled execution.\nprofile=no\n\n# Add files or directories to the blacklist. They should be base names, not\n# paths.\nignore=\n\n# Pickle collected data for later comparisons.\npersistent=yes\n\n# List of plugins (as comma separated values of python modules names) to load,\n# usually to register additional checkers.\nload-plugins=\n\n# Deprecated. It was used to include message's id in output. Use --msg-template\n# instead.\n#include-ids=no\n\n# Deprecated. It was used to include symbolic ids of messages in output. Use\n# --msg-template instead.\n#symbols=no\n\n# Use multiple processes to speed up Pylint.\njobs=1\n\n# Allow loading of arbitrary C extensions. Extensions are imported into the\n# active Python interpreter and may run arbitrary code.\nunsafe-load-any-extension=no\n\n# A comma-separated list of package or module names from where C extensions may\n# be loaded. Extensions are loading into the active Python interpreter and may\n# run arbitrary code\nextension-pkg-whitelist=\n\n# Allow optimization of some AST trees. This will activate a peephole AST\n# optimizer, which will apply various small optimizations. For instance, it can\n# be used to obtain the result of joining multiple strings with the addition\n# operator. Joining a lot of strings can lead to a maximum recursion error in\n# Pylint and this flag can prevent that. It has one side effect, the resulting\n# AST will be different than the one from reality.\noptimize-ast=no\n\n\n[MESSAGES CONTROL]\n\n# Only show warnings with the listed confidence levels. Leave empty to show\n# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED\nconfidence=\n\n# Enable the message, report, category or checker with the given id(s). You can\n# either give multiple identifier separated by comma (,) or put this option\n# multiple time. See also the \"--disable\" option for examples.\n#enable=\n\n# Disable the message, report, category or checker with the given id(s). You\n# can either give multiple identifiers separated by comma (,) or put this\n# option multiple times (only on the command line, not in the configuration\n# file where it should appear only once).You can also use \"--disable=all\" to\n# disable everything first and then reenable specific checks. For example, if\n# you want to run only the similarities checker, you can use \"--disable=all\n# --enable=similarities\". If you want to run only the classes checker, but have\n# no Warning level messages displayed, use\"--disable=all --enable=classes\n# --disable=W\"\ndisable=E1604,W1629,W1605,I0020,W1609,W1615,W1610,W1618,W1608,W1622,W1640,E1603,W1635,W1636,W1634,W1628,W1614,E1601,W1601,I0021,E1605,W1611,W1612,W1619,W1616,W1638,W1626,W1630,W1607,E1602,W1623,W1613,W1606,W1625,W0704,W1639,W1603,W1632,E1606,W1602,W1637,W1624,W1620,E1608,W1627,E1607,W1633,W1604,W1617,W1621\n\n\n[REPORTS]\n\n# Set the output format. Available formats are text, parseable, colorized, msvs\n# (visual studio) and html. You can also give a reporter class, eg\n# mypackage.mymodule.MyReporterClass.\noutput-format=text\n\n# Put messages in a separate file for each module / package specified on the\n# command line instead of printing them on stdout. Reports (if any) will be\n# written in a file name \"pylint_global.[txt|html]\".\nfiles-output=no\n\n# Tells whether to display a full report or only the messages\nreports=yes\n\n# Python expression which should return a note less than 10 (10 is the highest\n# note). You have access to the variables errors warning, statement which\n# respectively contain the number of errors / warnings messages and the total\n# number of statements analyzed. This is used by the global evaluation report\n# (RP0004).\nevaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)\n\n# Add a comment according to your evaluation note. This is used by the global\n# evaluation report (RP0004).\ncomment=no\n\n# Template used to display messages. This is a python new-style format string\n# used to format the message information. See doc for all details.\n# Path/line on separate added to allow IDE to parse error messages and\n# provide interactive link on the place of the error.\nmsg-template={path}:{line}:\n    {C}:{msg_id}:{line:3d},{column:2d}: {msg} ({symbol})\n\n\n[LOGGING]\n\n# Logging modules to check that the string format arguments are in logging\n# function parameter format\nlogging-modules=logging\n\n\n[FORMAT]\n\n# Maximum number of characters on a single line.\nmax-line-length=100\n\n# Regexp for a line that is allowed to be longer than the limit.\nignore-long-lines=^\\s*(# )?<?https?://\\S+>?$\n\n# Allow the body of an if to be on the same line as the test if there is no\n# else.\nsingle-line-if-stmt=no\n\n# List of optional constructs for which whitespace checking is disabled\nno-space-check=trailing-comma,dict-separator\n\n# Maximum number of lines in a module\nmax-module-lines=1000\n\n# String used as indentation unit. This is usually \" \" (4 spaces) or \"\\t\" (1\n# tab).\nindent-string='    '\n\n# Number of spaces of indent required inside a hanging or continued line.\nindent-after-paren=4\n\n# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.\nexpected-line-ending-format=\n\n\n[MISCELLANEOUS]\n\n# List of note tags to take in consideration, separated by a comma.\nnotes=FIXME,XXX,TODO\n\n\n[VARIABLES]\n\n# Tells whether we should check for unused import in __init__ files.\ninit-import=no\n\n# A regular expression matching the name of dummy variables (i.e. expectedly\n# not used).\ndummy-variables-rgx=_$|dummy\n\n# List of additional names supposed to be defined in builtins. Remember that\n# you should avoid to define new builtins when possible.\nadditional-builtins=\n\n# List of strings which can identify a callback function by name. A callback\n# name must start or end with one of those strings.\ncallbacks=cb_,_cb\n\n\n[BASIC]\n\n# Required attributes for module, separated by a comma\nrequired-attributes=\n\n# List of builtins function names that should not be used, separated by a comma\nbad-functions=map,filter\n\n# Good variable names which should always be accepted, separated by a comma\ngood-names=i,j,k,ex,Run,_\n\n# Bad variable names which should always be refused, separated by a comma\nbad-names=foo,bar,baz,toto,tutu,tata\n\n# Colon-delimited sets of names that determine each other's naming style when\n# the name regexes allow several styles.\nname-group=\n\n# Include a hint for the correct naming format with invalid-name\ninclude-naming-hint=no\n\n# Regular expression matching correct module names\nmodule-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$\n\n# Naming hint for module names\nmodule-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$\n\n# Regular expression matching correct method names\nmethod-rgx=[a-z_][a-z0-9_]{2,30}$\n\n# Naming hint for method names\nmethod-name-hint=[a-z_][a-z0-9_]{2,30}$\n\n# Regular expression matching correct class attribute names\nclass-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$\n\n# Naming hint for class attribute names\nclass-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$\n\n# Regular expression matching correct class names\nclass-rgx=[A-Z_][a-zA-Z0-9]+$\n\n# Naming hint for class names\nclass-name-hint=[A-Z_][a-zA-Z0-9]+$\n\n# Regular expression matching correct function names\nfunction-rgx=[a-z_][a-z0-9_]{2,30}$\n\n# Naming hint for function names\nfunction-name-hint=[a-z_][a-z0-9_]{2,30}$\n\n# Regular expression matching correct inline iteration names\ninlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$\n\n# Naming hint for inline iteration names\ninlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$\n\n# Regular expression matching correct constant names\nconst-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$\n\n# Naming hint for constant names\nconst-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$\n\n# Regular expression matching correct argument names\nargument-rgx=[a-z_][a-z0-9_]{2,30}$\n\n# Naming hint for argument names\nargument-name-hint=[a-z_][a-z0-9_]{2,30}$\n\n# Regular expression matching correct attribute names\nattr-rgx=[a-z_][a-z0-9_]{2,30}$\n\n# Naming hint for attribute names\nattr-name-hint=[a-z_][a-z0-9_]{2,30}$\n\n# Regular expression matching correct variable names\nvariable-rgx=[a-z_][a-z0-9_]{2,30}$\n\n# Naming hint for variable names\nvariable-name-hint=[a-z_][a-z0-9_]{2,30}$\n\n# Regular expression which should only match function or class names that do\n# not require a docstring.\nno-docstring-rgx=__.*__\n\n# Minimum line length for functions/classes that require docstrings, shorter\n# ones are exempt.\ndocstring-min-length=-1\n\n\n[TYPECHECK]\n\n# Tells whether missing members accessed in mixin class should be ignored. A\n# mixin class is detected if its name ends with \"mixin\" (case insensitive).\nignore-mixin-members=yes\n\n# List of module names for which member attributes should not be checked\n# (useful for modules/projects where namespaces are manipulated during runtime\n# and thus existing member attributes cannot be deduced by static analysis\nignored-modules=\n\n# List of classes names for which member attributes should not be checked\n# (useful for classes with attributes dynamically set).\nignored-classes=SQLObject\n\n# When zope mode is activated, add a predefined set of Zope acquired attributes\n# to generated-members.\nzope=no\n\n# List of members which are set dynamically and missed by pylint inference\n# system, and so shouldn't trigger E0201 when accessed. Python regular\n# expressions are accepted.\ngenerated-members=REQUEST,acl_users,aq_parent\n\n\n[SPELLING]\n\n# Spelling dictionary name. Available dictionaries: none. To make it working\n# install python-enchant package.\nspelling-dict=\n\n# List of comma separated words that should not be checked.\nspelling-ignore-words=\n\n# A path to a file that contains private dictionary; one word per line.\nspelling-private-dict-file=\n\n# Tells whether to store unknown words to indicated private dictionary in\n# --spelling-private-dict-file option instead of raising a message.\nspelling-store-unknown-words=no\n\n\n[SIMILARITIES]\n\n# Minimum lines number of a similarity.\nmin-similarity-lines=4\n\n# Ignore comments when computing similarities.\nignore-comments=yes\n\n# Ignore docstrings when computing similarities.\nignore-docstrings=yes\n\n# Ignore imports when computing similarities.\nignore-imports=no\n\n\n[IMPORTS]\n\n# Deprecated modules which should not be used, separated by a comma\ndeprecated-modules=stringprep,optparse\n\n# Create a graph of every (i.e. internal and external) dependencies in the\n# given file (report RP0402 must not be disabled)\nimport-graph=\n\n# Create a graph of external dependencies in the given file (report RP0402 must\n# not be disabled)\next-import-graph=\n\n# Create a graph of internal dependencies in the given file (report RP0402 must\n# not be disabled)\nint-import-graph=\n\n\n[CLASSES]\n\n# List of interface methods to ignore, separated by a comma. This is used for\n# instance to not check methods defines in Zope's Interface base class.\nignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by\n\n# List of method names used to declare (i.e. assign) instance attributes.\ndefining-attr-methods=__init__,__new__,setUp\n\n# List of valid names for the first argument in a class method.\nvalid-classmethod-first-arg=cls\n\n# List of valid names for the first argument in a metaclass class method.\nvalid-metaclass-classmethod-first-arg=mcs\n\n# List of member names, which should be excluded from the protected access\n# warning.\nexclude-protected=_asdict,_fields,_replace,_source,_make\n\n\n[DESIGN]\n\n# Maximum number of arguments for function / method\nmax-args=5\n\n# Argument names that match this expression will be ignored. Default to name\n# with leading underscore\nignored-argument-names=_.*\n\n# Maximum number of locals for function / method body\nmax-locals=15\n\n# Maximum number of return / yield for function / method body\nmax-returns=6\n\n# Maximum number of branch for function / method body\nmax-branches=12\n\n# Maximum number of statements in function / method body\nmax-statements=50\n\n# Maximum number of parents for a class (see R0901).\nmax-parents=7\n\n# Maximum number of attributes for a class (see R0902).\nmax-attributes=7\n\n# Minimum number of public methods for a class (see R0903).\nmin-public-methods=2\n\n# Maximum number of public methods for a class (see R0904).\nmax-public-methods=20\n\n\n[EXCEPTIONS]\n\n# Exceptions that will emit a warning when being caught. Defaults to\n# \"Exception\"\novergeneral-exceptions=Exception\n"
  },
  {
    "path": ".pyup.yml",
    "content": "# Label PRs with `deps-update` label\nlabel_prs: deps-update\n\nschedule: every week\n"
  },
  {
    "path": "CHANGES.rst",
    "content": "=========\n CHANGES\n=========\n\n0.8.1 (2025-03-31)\n==================\n\n- Fix packaging to not install on Python 3.8.\n\n0.8.0 (2025-03-11)\n==================\n\n- Make the library compatible with aiohttp 3.9+ and Python 3.9+\n\n0.7.0 (2018-03-05)\n==================\n\n- Make web view check implicit and type based (#159)\n\n- Disable Python 3.4 support (#156)\n\n- Support aiohttp 3.0+ (#155)\n\n0.6.0 (2017-12-21)\n==================\n\n- Support aiohttp views by ``CorsViewMixin`` (#145)\n\n0.5.3 (2017-04-21)\n==================\n\n- Fix ``typing`` being installed on Python 3.6.\n\n0.5.2 (2017-03-28)\n==================\n\n- Fix tests compatibility with ``aiohttp`` 2.0.\n  This release and release v0.5.0 should work on ``aiohttp`` 2.0.\n\n\n0.5.1 (2017-03-23)\n==================\n\n- Enforce ``aiohttp`` version to be less than 2.0.\n  Newer ``aiohttp`` releases will be supported in the next release.\n\n0.5.0 (2016-11-18)\n==================\n\n- Fix compatibility with ``aiohttp`` 1.1\n\n\n0.4.0 (2016-04-04)\n==================\n\n- Fixed support with new Resources objects introduced in ``aiohttp`` 0.21.0.\n  Minimum supported version of ``aiohttp`` is 0.21.4 now.\n\n- New Resources objects are supported.\n  You can specify default configuration for a Resource and use\n  ``allow_methods`` to explicitly list allowed methods (or ``*`` for all\n  HTTP methods):\n\n  .. code-block:: python\n\n        # Allow POST and PUT requests from \"http://client.example.org\" origin.\n        hello_resource = cors.add(app.router.add_resource(\"/hello\"), {\n                \"http://client.example.org\":\n                    aiohttp_cors.ResourceOptions(\n                        allow_methods=[\"POST\", \"PUT\"]),\n            })\n        # No need to add POST and PUT routes into CORS configuration object.\n        hello_resource.add_route(\"POST\", handler_post)\n        hello_resource.add_route(\"PUT\", handler_put)\n        # Still you can add additional methods to CORS configuration object:\n        cors.add(hello_resource.add_route(\"DELETE\", handler_delete))\n\n- ``AbstractRouterAdapter`` was completely rewritten to be more Router\n  agnostic.\n\n0.3.0 (2016-02-06)\n==================\n\n- Rename ``UrlDistatcherRouterAdapter`` to ``UrlDispatcherRouterAdapter``.\n\n- Set maximum supported ``aiohttp`` version to ``0.20.2``, see bug #30 for\n  details.\n\n0.2.0 (2015-11-30)\n==================\n\n- Move ABCs from ``aiohttp_cors.router_adapter`` to ``aiohttp_cors.abc``.\n\n- Rename ``RouterAdapter`` to ``AbstractRouterAdapter``.\n\n- Fix bug with configuring CORS for named routes.\n\n0.1.0 (2015-11-05)\n==================\n\n* Initial release.\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2015-2018 Vladimir Rutsky and aio-libs team\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "MANIFEST.in",
    "content": "include LICENSE\ninclude CHANGES.rst\ninclude README.rst\nrecursive-include tests *.py\ninclude tests/integration/test_page.html\n"
  },
  {
    "path": "Makefile",
    "content": "all: test\n\n\nlint:\n\tpre-commit run --all-files\n\ntest: lint\n\tpytest tests\n"
  },
  {
    "path": "README.rst",
    "content": "========================\nCORS support for aiohttp\n========================\n\n``aiohttp_cors`` library implements\n`Cross Origin Resource Sharing (CORS) <cors_>`__\nsupport for `aiohttp <aiohttp_>`__\nasyncio-powered asynchronous HTTP server.\n\nJump directly to `Usage`_ part to see how to use ``aiohttp_cors``.\n\nSame-origin policy\n==================\n\nWeb security model is tightly connected to\n`Same-origin policy (SOP) <sop_>`__.\nIn short: web pages cannot *Read* resources which origin\ndoesn't match origin of requested page, but can *Embed* (or *Execute*)\nresources and have limited ability to *Write* resources.\n\nOrigin of a page is defined in the `Standard <cors_>`__ as tuple\n``(schema, host, port)``\n(there is a notable exception with Internet Explorer: it doesn't use port to\ndefine origin, but uses it's own\n`Security Zones <https://msdn.microsoft.com/en-us/library/ms537183.aspx>`__).\n\nCan *Embed* means that resource from other origin can be embedded into\nthe page,\ne.g. by using ``<script src=\"...\">``, ``<img src=\"...\">``,\n``<iframe src=\"...\">``.\n\nCannot *Read* means that resource from other origin *source* cannot be\nobtained by page\n(*source* — any information that would allow to reconstruct resource).\nE.g. the page can *Embed* image with ``<img src=\"...\">``,\nbut it can't get information about specific pixels, so page can't reconstruct\noriginal image\n(though some information from the other resource may still be leaked:\ne.g. the page can read embedded image dimensions).\n\nLimited ability to *Write* means, that the page can send POST requests to\nother origin with limited set of ``Content-Type`` values and headers.\n\nRestriction to *Read* resource from other origin is related to authentication\nmechanism that is used by browsers:\nwhen browser reads (downloads) resource he automatically sends all security\ncredentials that user previously authorized for that resource\n(e.g. cookies, HTTP Basic Authentication).\n\nFor example, if *Read* would be allowed and user is authenticated\nin some internet banking,\nmalicious page would be able to embed internet banking page with ``iframe``\n(since authentication is done by the browser it may be embedded as if\nuser is directly navigated to internet banking page),\nthen read user private information by reading *source* of the embedded page\n(which may be not only source code, but, for example,\nscreenshot of the embedded internet banking page).\n\nCross-origin resource sharing\n=============================\n\n`Cross-origin Resource Sharing (CORS) <cors_>`__ allows to override\nSOP for specific resources.\n\nIn short, CORS works in the following way.\n\nWhen page ``https://client.example.com`` request (*Read*) resource\n``https://server.example.com/resource`` that have other origin,\nbrowser implicitly appends ``Origin: https://client.example.com`` header\nto the HTTP request,\neffectively requesting server to give read permission for\nthe resource to the ``https://client.example.com`` page::\n\n    GET /resource HTTP/1.1\n    Origin: https://client.example.com\n    Host: server.example.com\n\nIf server allows access from the page to the resource, it responds with\nresource with ``Access-Control-Allow-Origin: https://client.example.com``\nHTTP header\n(optionally allowing exposing custom server headers to the page and\nenabling use of the user credentials on the server resource)::\n\n    Access-Control-Allow-Origin: https://client.example.com\n    Access-Control-Allow-Credentials: true\n    Access-Control-Expose-Headers: X-Server-Header\n\nBrowser checks, if server responded with proper\n``Access-Control-Allow-Origin`` header and accordingly allows or denies\naccess for the obtained resource to the page.\n\nCORS specification designed in a way that servers that are not aware\nof CORS will not expose any additional information, except allowed by the\nSOP.\n\nTo request resources with custom headers or using custom HTTP methods\n(e.g. ``PUT``, ``DELETE``) that are not allowed by SOP,\nCORS-enabled browser first send *preflight request* to the\nresource using ``OPTIONS`` method, in which he queries access to the resource\nwith specific method and headers::\n\n    OPTIONS / HTTP/1.1\n    Origin: https://client.example.com\n    Access-Control-Request-Method: PUT\n    Access-Control-Request-Headers: X-Client-Header\n\nCORS-enabled server responds is requested method is allowed and which of\nthe specified headers are allowed::\n\n    Access-Control-Allow-Origin: https://client.example.com\n    Access-Control-Allow-Credentials: true\n    Access-Control-Allow-Methods: PUT\n    Access-Control-Allow-Headers: X-Client-Header\n    Access-Control-Max-Age: 3600\n\nBrowser checks response to preflight request, and, if actual request allowed,\ndoes actual request.\n\nInstallation\n============\n\nYou can install ``aiohttp_cors`` as a typical Python library from PyPI or\nfrom git:\n\n.. code-block:: bash\n\n    $ pip install aiohttp_cors\n\nUsage\n=====\n\nTo use ``aiohttp_cors`` you need to configure the application and\nenable CORS on\n`resources and routes <https://aiohttp.readthedocs.org/en/stable/web.html#resources-and-routes>`__\nthat you want to expose:\n\n.. code-block:: python\n\n    import asyncio\n    from aiohttp import web\n    import aiohttp_cors\n\n    @asyncio.coroutine\n    def handler(request):\n        return web.Response(\n            text=\"Hello!\",\n            headers={\n                \"X-Custom-Server-Header\": \"Custom data\",\n            })\n\n    app = web.Application()\n\n    # `aiohttp_cors.setup` returns `aiohttp_cors.CorsConfig` instance.\n    # The `cors` instance will store CORS configuration for the\n    # application.\n    cors = aiohttp_cors.setup(app)\n\n    # To enable CORS processing for specific route you need to add\n    # that route to the CORS configuration object and specify its\n    # CORS options.\n    resource = cors.add(app.router.add_resource(\"/hello\"))\n    route = cors.add(\n        resource.add_route(\"GET\", handler), {\n            \"http://client.example.org\": aiohttp_cors.ResourceOptions(\n                allow_credentials=True,\n                expose_headers=(\"X-Custom-Server-Header\",),\n                allow_headers=(\"X-Requested-With\", \"Content-Type\"),\n                max_age=3600,\n            )\n        })\n\nEach route has it's own CORS configuration passed in ``CorsConfig.add()``\nmethod.\n\nCORS configuration is a mapping from origins to options for that origins.\n\nIn the example above CORS is configured for the resource under path ``/hello``\nand HTTP method ``GET``, and in the context of CORS:\n\n* This resource will be available using CORS only to\n  ``http://client.example.org`` origin.\n\n* Passing of credentials to this resource will be allowed.\n\n* The resource will expose to the client ``X-Custom-Server-Header``\n  server header.\n\n* The client will be allowed to pass ``X-Requested-With`` and\n  ``Content-Type`` headers to the server.\n\n* Preflight requests will be allowed to be cached by client for ``3600``\n  seconds.\n\nResource will be available only to the explicitly specified origins.\nYou can specify \"all other origins\" using special ``*`` origin:\n\n.. code-block:: python\n\n    cors.add(route, {\n            \"*\":\n                aiohttp_cors.ResourceOptions(allow_credentials=False),\n            \"http://client.example.org\":\n                aiohttp_cors.ResourceOptions(allow_credentials=True),\n        })\n\nHere the resource specified by ``route`` will be available to all origins with\ndisallowed credentials passing, and with allowed credentials passing only to\n``http://client.example.org``.\n\nBy default ``ResourceOptions`` will be constructed without any allowed CORS\noptions.\nThis means, that resource will be available using CORS to specified origin,\nbut client will not be allowed to send either credentials,\nor send non-simple headers, or read from server non-simple headers.\n\nTo enable sending or receiving all headers you can specify special value\n``*`` instead of sequence of headers:\n\n.. code-block:: python\n\n    cors.add(route, {\n            \"http://client.example.org\":\n                aiohttp_cors.ResourceOptions(\n                    expose_headers=\"*\",\n                    allow_headers=\"*\"),\n        })\n\nYou can specify default CORS-enabled resource options using\n``aiohttp_cors.setup()``'s ``defaults`` argument:\n\n.. code-block:: python\n\n    cors = aiohttp_cors.setup(app, defaults={\n            # Allow all to read all CORS-enabled resources from\n            # http://client.example.org.\n            \"http://client.example.org\": aiohttp_cors.ResourceOptions(),\n        })\n\n    # Enable CORS on routes.\n\n    # According to defaults POST and PUT will be available only to\n    # \"http://client.example.org\".\n    hello_resource = cors.add(app.router.add_resource(\"/hello\"))\n    cors.add(hello_resource.add_route(\"POST\", handler_post))\n    cors.add(hello_resource.add_route(\"PUT\", handler_put))\n\n    # In addition to \"http://client.example.org\", GET request will be\n    # allowed from \"http://other-client.example.org\" origin.\n    cors.add(hello_resource.add_route(\"GET\", handler), {\n            \"http://other-client.example.org\":\n                aiohttp_cors.ResourceOptions(),\n        })\n\n    # CORS will be enabled only on the resources added to `CorsConfig`,\n    # so following resource will be NOT CORS-enabled.\n    app.router.add_route(\"GET\", \"/private\", handler)\n\nAlso you can specify default options for resources:\n\n.. code-block:: python\n\n    # Allow POST and PUT requests from \"http://client.example.org\" origin.\n    hello_resource = cors.add(app.router.add_resource(\"/hello\"), {\n            \"http://client.example.org\": aiohttp_cors.ResourceOptions(),\n        })\n    cors.add(hello_resource.add_route(\"POST\", handler_post))\n    cors.add(hello_resource.add_route(\"PUT\", handler_put))\n\nResource CORS configuration allows to use ``allow_methods`` option that\nexplicitly specifies list of allowed HTTP methods for origin\n(or ``*`` for all HTTP methods).\nBy using this option it is not required to add all resource routes to\nCORS configuration object:\n\n.. code-block:: python\n\n    # Allow POST and PUT requests from \"http://client.example.org\" origin.\n    hello_resource = cors.add(app.router.add_resource(\"/hello\"), {\n            \"http://client.example.org\":\n                aiohttp_cors.ResourceOptions(allow_methods=[\"POST\", \"PUT\"]),\n        })\n    # No need to add POST and PUT routes into CORS configuration object.\n    hello_resource.add_route(\"POST\", handler_post)\n    hello_resource.add_route(\"PUT\", handler_put)\n    # Still you can add additional methods to CORS configuration object:\n    cors.add(hello_resource.add_route(\"DELETE\", handler_delete))\n\nHere is an example of how to enable CORS for all origins with all CORS\nfeatures:\n\n.. code-block:: python\n\n    cors = aiohttp_cors.setup(app, defaults={\n        \"*\": aiohttp_cors.ResourceOptions(\n                allow_credentials=True,\n                expose_headers=\"*\",\n                allow_headers=\"*\",\n            )\n    })\n\n    # Add all resources to `CorsConfig`.\n    resource = cors.add(app.router.add_resource(\"/hello\"))\n    cors.add(resource.add_route(\"GET\", handler_get))\n    cors.add(resource.add_route(\"PUT\", handler_put))\n    cors.add(resource.add_route(\"POST\", handler_put))\n    cors.add(resource.add_route(\"DELETE\", handler_delete))\n\nOld routes API is supported — you can use ``router.add_router`` and\n``router.register_route`` as before, though this usage is discouraged:\n\n.. code-block:: python\n\n    cors.add(\n        app.router.add_route(\"GET\", \"/hello\", handler), {\n            \"http://client.example.org\": aiohttp_cors.ResourceOptions(\n                allow_credentials=True,\n                expose_headers=(\"X-Custom-Server-Header\",),\n                allow_headers=(\"X-Requested-With\", \"Content-Type\"),\n                max_age=3600,\n            )\n        })\n\nYou can enable CORS for all added routes by accessing routes list\nin the router:\n\n.. code-block:: python\n\n    # Setup application routes.\n    app.router.add_route(\"GET\", \"/hello\", handler_get)\n    app.router.add_route(\"PUT\", \"/hello\", handler_put)\n    app.router.add_route(\"POST\", \"/hello\", handler_put)\n    app.router.add_route(\"DELETE\", \"/hello\", handler_delete)\n\n    # Configure default CORS settings.\n    cors = aiohttp_cors.setup(app, defaults={\n        \"*\": aiohttp_cors.ResourceOptions(\n                allow_credentials=True,\n                expose_headers=\"*\",\n                allow_headers=\"*\",\n            )\n    })\n\n    # Configure CORS on all routes.\n    for route in list(app.router.routes()):\n        cors.add(route)\n\nYou can also use ``CorsViewMixin`` on ``web.View``:\n\n.. code-block:: python\n\n    class CorsView(web.View, CorsViewMixin):\n\n        cors_config = {\n            \"*\": ResourceOption(\n                allow_credentials=True,\n                allow_headers=\"X-Request-ID\",\n            )\n        }\n\n        @asyncio.coroutine\n        def get(self):\n            return web.Response(text=\"Done\")\n\n        @custom_cors({\n            \"*\": ResourceOption(\n                allow_credentials=True,\n                allow_headers=\"*\",\n            )\n        })\n        @asyncio.coroutine\n        def post(self):\n            return web.Response(text=\"Done\")\n\n    cors = aiohttp_cors.setup(app, defaults={\n        \"*\": aiohttp_cors.ResourceOptions(\n                allow_credentials=True,\n                expose_headers=\"*\",\n                allow_headers=\"*\",\n            )\n    })\n\n    cors.add(\n        app.router.add_route(\"*\", \"/resource\", CorsView),\n        webview=True)\n\n\nSecurity\n========\n\nTODO: fill this\n\nDevelopment\n===========\n\nTo setup development environment:\n\n.. code-block:: bash\n\n   # Clone sources repository:\n   git clone https://github.com/aio-libs/aiohttp_cors.git .\n   # Create and activate virtual Python environment:\n   python3 -m venv env\n   source env/bin/activate\n   # Install requirements and aiohttp_cors into virtual environment\n   pip install -r requirements-dev.txt\n\nTo run tests:\n\n.. code-block:: bash\n\n   tox\n\nTo run only runtime tests in current environment:\n\n.. code-block:: bash\n\n   py.test\n\nTo run only static code analysis checks:\n\n.. code-block:: bash\n\n   tox -e check\n\nRunning Selenium tests\n----------------------\n\nTo run Selenium tests with Firefox web driver you need to install Firefox.\n\nTo run Selenium tests with Chromium web driver you need to:\n\n1. Install Chrome driver. On Ubuntu 14.04 it's in ``chromium-chromedriver``\n   package.\n\n2. Either add ``chromedriver`` to PATH or set ``WEBDRIVER_CHROMEDRIVER_PATH``\n   environment variable to ``chromedriver``, e.g. on Ubuntu 14.04\n   ``WEBDRIVER_CHROMEDRIVER_PATH=/usr/lib/chromium-browser/chromedriver``.\n\nRelease process\n---------------\n\nTo release version ``vA.B.C`` from the current version of ``master`` branch\nyou need to:\n\n1. Create local branch ``vA.B.C``.\n2. In ``CHANGES.rst`` set release date to today.\n3. In ``aiohttp_cors/__about__.py`` change version from ``A.B.Ca0`` to\n   ``A.B.C``.\n4. Create pull request with ``vA.B.C`` branch, wait for all checks to\n   successfully finish (Travis and Appveyor).\n5. Merge pull request to master.\n6. Update and checkout ``master`` branch.\n\n7. Create and push tag for release version to GitHub:\n\n   .. code-block:: bash\n\n      git tag vA.B.C\n      git push --tags\n\n   Now Travis should ran tests again, and build and deploy wheel on PyPI.\n\n   If Travis release doesn't work for some reason, use following steps\n   for manual release upload.\n\n   1. Install fresh versions of setuptools and pip.\n      Install ``wheel`` for building wheels.\n      Install ``twine`` for uploading to PyPI.\n\n      .. code-block:: bash\n\n         pip install -U pip setuptools twine wheel\n\n   2. Configure PyPI credentials in ``~/.pypirc``.\n\n   3. Build distribution:\n\n      .. code-block:: bash\n\n         rm -rf build dist; python setup.py sdist bdist_wheel\n\n   4. Upload new release to PyPI:\n\n      .. code-block:: bash\n\n         twine upload dist/*\n\n8. Edit release description on GitHub if needed.\n9. Announce new release on the *aio-libs* mailing list:\n   https://groups.google.com/forum/#!forum/aio-libs.\n\nPost release steps:\n\n1. In ``CHANGES.rst`` add template for the next release.\n2. In ``aiohttp_cors/__about__.py`` change version from ``A.B.C`` to\n   ``A.(B + 1).0a0``.\n\nBugs\n====\n\nPlease report bugs, issues, feature requests, etc. on\n`GitHub <https://github.com/aio-libs/aiohttp_cors/issues>`__.\n\n\nLicense\n=======\n\nCopyright 2015 Vladimir Rutsky <vladimir@rutsky.org>.\n\nLicensed under the\n`Apache License, Version 2.0 <https://www.apache.org/licenses/LICENSE-2.0>`__,\nsee ``LICENSE`` file for details.\n\n.. _cors: http://www.w3.org/TR/cors/\n.. _aiohttp: https://github.com/KeepSafe/aiohttp/\n.. _sop: https://en.wikipedia.org/wiki/Same-origin_policy\n"
  },
  {
    "path": "aiohttp_cors/__about__.py",
    "content": "# Copyright 2015 Vladimir Rutsky <vladimir@rutsky.org>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nLibrary meta information.\n\nThis module must be stand-alone executable.\n\"\"\"\n\n__title__ = \"aiohttp-cors\"\n__version__ = \"0.8.1\"\n__author__ = \"Vladimir Rutsky and aio-libs team\"\n__email__ = \"vladimir@rutsky.org\"\n__summary__ = \"CORS support for aiohttp\"\n__uri__ = \"https://github.com/aio-libs/aiohttp-cors\"\n__license__ = \"Apache License, Version 2.0\"\n__copyright__ = \"2015 aio-libs team\"\n"
  },
  {
    "path": "aiohttp_cors/__init__.py",
    "content": "# Copyright 2015 Vladimir Rutsky <vladimir@rutsky.org>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"CORS support for aiohttp.\"\"\"\n\nfrom collections.abc import Mapping\nfrom typing import Any, Union\n\nfrom aiohttp import web\n\nfrom .__about__ import (\n    __author__,\n    __copyright__,\n    __email__,\n    __license__,\n    __summary__,\n    __title__,\n    __uri__,\n    __version__,\n)\nfrom .cors_config import CorsConfig\nfrom .mixin import CorsViewMixin, custom_cors\nfrom .resource_options import ResourceOptions\n\n__all__ = (\n    \"CorsConfig\",\n    \"CorsViewMixin\",\n    \"ResourceOptions\",\n    \"__author__\",\n    \"__copyright__\",\n    \"__email__\",\n    \"__license__\",\n    \"__summary__\",\n    \"__title__\",\n    \"__uri__\",\n    \"__version__\",\n    \"custom_cors\",\n    \"setup\",\n)\n\n\nAPP_CONFIG_KEY: web.AppKey[CorsConfig] = web.AppKey(\"aiohttp_cors\", CorsConfig)\n\n\ndef setup(\n    app: web.Application,\n    *,\n    defaults: Mapping[str, Union[ResourceOptions, Mapping[str, Any]]] = None\n) -> CorsConfig:\n    \"\"\"Setup CORS processing for the application.\n\n    To enable CORS for a resource you need to explicitly add route for\n    that resource using `CorsConfig.add()` method::\n\n        app = aiohttp.web.Application()\n        cors = aiohttp_cors.setup(app)\n        cors.add(\n            app.router.add_route(\"GET\", \"/resource\", handler),\n            {\n                \"*\": aiohttp_cors.ResourceOptions(\n                    allow_credentials=True,\n                    expose_headers=\"*\",\n                    allow_headers=\"*\"),\n            })\n\n    :param app:\n        The application for which CORS will be configured.\n    :param defaults:\n        Default settings for origins.\n    )\n    \"\"\"\n    cors = CorsConfig(app, defaults=defaults)\n    app[APP_CONFIG_KEY] = cors\n    return cors\n"
  },
  {
    "path": "aiohttp_cors/abc.py",
    "content": "# Copyright 2015 Vladimir Rutsky <vladimir@rutsky.org>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Abstract base classes.\"\"\"\n\nfrom abc import ABCMeta, abstractmethod\n\nfrom aiohttp import web\n\n__all__ = (\"AbstractRouterAdapter\",)\n\n\nclass AbstractRouterAdapter(metaclass=ABCMeta):\n    \"\"\"Router adapter for handling CORS configuration interface.\n\n    `AbstractRouter` doesn't specify how HTTP requests are delivered\n    to handlers, and aiohttp_cors doesn't rely on specific implementation\n    details.\n\n    In general Router can be seen as a substance that allows to setup handlers\n    for specific HTTP methods and requests paths, lets call these Router's\n    items routing entities.\n    Generic Router is configured with set of routing entities and their\n    handlers.\n\n    This adapter assumes that its reasonable to configure CORS for same\n    routing entities as used in `AbstractRouter`.\n    Routing entities will be added to CorsConfig to enable CORS for them.\n\n    For example, for aiohttp < 0.21.0 routing entity would be\n    `aiohttp.web.Route` — tuple of (HTTP method, URI path).\n    And CORS can be configured for each `aiohttp.web.Route`.\n\n    In aiohttp >= 0.21.0 there are two routing entities: Resource and Route.\n    You can configure CORS for Resource (which will be interpreted as default\n    for all Routes on Resources), and configure CORS for specific Route.\n    \"\"\"\n\n    @abstractmethod\n    def add_preflight_handler(self, routing_entity, handler, webview: bool = False):\n        \"\"\"Add OPTIONS handler for all routes defined by `routing_entity`.\n\n        Does nothing if CORS handler already handles routing entity.\n        Should fail if there are conflicting user-defined OPTIONS handlers.\n        \"\"\"\n\n    @abstractmethod\n    def is_preflight_request(self, request: web.Request) -> bool:\n        \"\"\"Is `request` is a CORS preflight request.\"\"\"\n\n    @abstractmethod\n    def is_cors_enabled_on_request(self, request: web.Request) -> bool:\n        \"\"\"Is `request` is a request for CORS-enabled resource.\"\"\"\n\n    @abstractmethod\n    def set_config_for_routing_entity(self, routing_entity, config):\n        \"\"\"Record configuration for routing entity.\n\n        If router implements hierarchical routing entities, stored config\n        can be used in hierarchical manner too.\n\n        Should raise if there is conflicting configuration for the routing\n        entity.\n        \"\"\"\n\n    @abstractmethod\n    async def get_preflight_request_config(\n        self, preflight_request: web.Request, origin: str, requested_method: str\n    ):\n        \"\"\"Get stored CORS configuration for specified HTTP method and origin\n        that corresponds to preflight request.\n\n        Should raise KeyError if CORS is not configured or not enabled\n        for specified HTTP method.\n        \"\"\"\n\n    @abstractmethod\n    def get_non_preflight_request_config(self, request: web.Request):\n        \"\"\"Get stored CORS configuration for routing entity that handles\n        specified request.\"\"\"\n"
  },
  {
    "path": "aiohttp_cors/cors_config.py",
    "content": "# Copyright 2015 Vladimir Rutsky <vladimir@rutsky.org>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"CORS configuration container class definition.\"\"\"\n\nimport collections\nimport warnings\nfrom collections.abc import Mapping\nfrom typing import Any, Union\n\nfrom aiohttp import hdrs, web\n\nfrom .abc import AbstractRouterAdapter\nfrom .preflight_handler import _PreflightHandler\nfrom .resource_options import ResourceOptions\nfrom .urldispatcher_router_adapter import ResourcesUrlDispatcherRouterAdapter\n\n__all__ = (\"CorsConfig\",)\n\n# Positive response to Access-Control-Allow-Credentials\n_TRUE = \"true\"\n# CORS simple response headers:\n# <http://www.w3.org/TR/cors/#simple-response-header>\n_SIMPLE_RESPONSE_HEADERS = frozenset(\n    [\n        hdrs.CACHE_CONTROL,\n        hdrs.CONTENT_LANGUAGE,\n        hdrs.CONTENT_TYPE,\n        hdrs.EXPIRES,\n        hdrs.LAST_MODIFIED,\n        hdrs.PRAGMA,\n    ]\n)\n\n\ndef _parse_config_options(\n    config: Mapping[str, Union[ResourceOptions, Mapping[str, Any]]] = None,\n):\n    \"\"\"Parse CORS configuration (default or per-route)\n\n    :param config:\n        Mapping from Origin to Resource configuration (allowed headers etc)\n        defined either as mapping or `ResourceOptions` instance.\n\n    Raises `ValueError` if configuration is not correct.\n    \"\"\"\n\n    if config is None:\n        return {}\n\n    if not isinstance(config, collections.abc.Mapping):\n        raise ValueError(f\"Config must be mapping, got '{config}'\")\n\n    parsed = {}\n\n    options_keys = {\"allow_credentials\", \"expose_headers\", \"allow_headers\", \"max_age\"}\n\n    for origin, options in config.items():\n        # TODO: check that all origins are properly formatted.\n        # This is not a security issue, since origin is compared as strings.\n        if not isinstance(origin, str):\n            raise ValueError(f\"Origin must be string, got '{origin}'\")\n\n        if isinstance(options, ResourceOptions):\n            resource_options = options\n\n        else:\n            if not isinstance(options, collections.abc.Mapping):\n                raise ValueError(\n                    \"Origin options must be either \"\n                    \"aiohttp_cors.ResourceOptions instance or mapping, \"\n                    \"got '{}'\".format(options)\n                )\n\n            unexpected_args = frozenset(options.keys()) - options_keys\n            if unexpected_args:\n                raise ValueError(\n                    \"Unexpected keywords in resource options: {}\".format(\n                        # pylint: disable=bad-builtin\n                        \",\".join(map(str, unexpected_args))\n                    )\n                )\n\n            resource_options = ResourceOptions(**options)\n\n        parsed[origin] = resource_options\n\n    return parsed\n\n\n_ConfigType = Mapping[str, Union[ResourceOptions, Mapping[str, Any]]]\n\n\nclass _CorsConfigImpl(_PreflightHandler):\n\n    def __init__(self, app: web.Application, router_adapter: AbstractRouterAdapter):\n        self._app = app\n\n        self._router_adapter = router_adapter\n\n        # Register hook for all responses.  This hook handles CORS-related\n        # headers on non-preflight requests.\n        self._app.on_response_prepare.append(self._on_response_prepare)\n\n    def add(self, routing_entity, config: _ConfigType = None):\n        \"\"\"Enable CORS for specific route or resource.\n\n        If route is passed CORS is enabled for route's resource.\n\n        :param routing_entity:\n            Route or Resource for which CORS should be enabled.\n        :param config:\n            CORS options for the route.\n        :return: `routing_entity`.\n        \"\"\"\n\n        parsed_config = _parse_config_options(config)\n\n        self._router_adapter.add_preflight_handler(\n            routing_entity, self._preflight_handler\n        )\n        self._router_adapter.set_config_for_routing_entity(\n            routing_entity, parsed_config\n        )\n\n        return routing_entity\n\n    async def _on_response_prepare(\n        self, request: web.Request, response: web.StreamResponse\n    ):\n        \"\"\"Non-preflight CORS request response processor.\n\n        If request is done on CORS-enabled route, process request parameters\n        and set appropriate CORS response headers.\n        \"\"\"\n        if not self._router_adapter.is_cors_enabled_on_request(\n            request\n        ) or self._router_adapter.is_preflight_request(request):\n            # Either not CORS enabled route, or preflight request which is\n            # handled in its own handler.\n            return\n\n        # Processing response of non-preflight CORS-enabled request.\n\n        config = self._router_adapter.get_non_preflight_request_config(request)\n\n        # Handle according to part 6.1 of the CORS specification.\n\n        origin = request.headers.get(hdrs.ORIGIN)\n        if origin is None:\n            # Terminate CORS according to CORS 6.1.1.\n            return\n\n        options = config.get(origin, config.get(\"*\"))\n        if options is None:\n            # Terminate CORS according to CORS 6.1.2.\n            return\n\n        assert hdrs.ACCESS_CONTROL_ALLOW_ORIGIN not in response.headers\n        assert hdrs.ACCESS_CONTROL_ALLOW_CREDENTIALS not in response.headers\n        assert hdrs.ACCESS_CONTROL_EXPOSE_HEADERS not in response.headers\n\n        # Process according to CORS 6.1.4.\n        # Set exposed headers (server headers exposed to client) before\n        # setting any other headers.\n        if options.expose_headers == \"*\":\n            # Expose all headers that are set in response.\n            exposed_headers = (\n                frozenset(response.headers.keys()) - _SIMPLE_RESPONSE_HEADERS\n            )\n            response.headers[hdrs.ACCESS_CONTROL_EXPOSE_HEADERS] = \",\".join(\n                exposed_headers\n            )\n\n        elif options.expose_headers:\n            # Expose predefined list of headers.\n            response.headers[hdrs.ACCESS_CONTROL_EXPOSE_HEADERS] = \",\".join(\n                options.expose_headers\n            )\n\n        # Process according to CORS 6.1.3.\n        # Set allowed origin.\n        response.headers[hdrs.ACCESS_CONTROL_ALLOW_ORIGIN] = origin\n        if options.allow_credentials:\n            # Set allowed credentials.\n            response.headers[hdrs.ACCESS_CONTROL_ALLOW_CREDENTIALS] = _TRUE\n\n    async def _get_config(self, request, origin, request_method):\n        config = await self._router_adapter.get_preflight_request_config(\n            request, origin, request_method\n        )\n        return config\n\n\nclass CorsConfig:\n    \"\"\"CORS configuration instance.\n\n    The instance holds default CORS parameters and per-route options specified\n    in `add()` method.\n\n    Each `aiohttp.web.Application` can have exactly one instance of this class.\n    \"\"\"\n\n    def __init__(\n        self,\n        app: web.Application,\n        *,\n        defaults: _ConfigType = None,\n        router_adapter: AbstractRouterAdapter = None,\n    ):\n        \"\"\"Construct CORS configuration.\n\n        :param app:\n            Application for which CORS configuration is built.\n        :param defaults:\n            Default CORS settings for origins.\n        :param router_adapter:\n            Router adapter. Required if application uses non-default router.\n        \"\"\"\n\n        self.defaults = _parse_config_options(defaults)\n\n        self._cors_impl = None\n\n        self._resources_router_adapter = None\n        self._resources_cors_impl = None\n\n        self._old_routes_cors_impl = None\n\n        if router_adapter is None:\n            router_adapter = ResourcesUrlDispatcherRouterAdapter(\n                app.router, self.defaults\n            )\n\n        self._cors_impl = _CorsConfigImpl(app, router_adapter)\n\n    def add(self, routing_entity, config: _ConfigType = None, webview: bool = False):\n        \"\"\"Enable CORS for specific route or resource.\n\n        If route is passed CORS is enabled for route's resource.\n\n        :param routing_entity:\n            Route or Resource for which CORS should be enabled.\n        :param config:\n            CORS options for the route.\n        :return: `routing_entity`.\n        \"\"\"\n\n        if webview:\n            warnings.warn(\n                \"webview argument is deprecated, \"\n                \"views are handled authomatically without \"\n                \"extra settings\",\n                DeprecationWarning,\n                stacklevel=2,\n            )\n\n        return self._cors_impl.add(routing_entity, config)\n"
  },
  {
    "path": "aiohttp_cors/mixin.py",
    "content": "import collections\n\nfrom .preflight_handler import _PreflightHandler\n\n\ndef custom_cors(config):\n    def wrapper(function):\n        name = f\"{function.__name__}_cors_config\"\n        setattr(function, name, config)\n        return function\n\n    return wrapper\n\n\nclass CorsViewMixin(_PreflightHandler):\n    cors_config = None\n\n    @classmethod\n    def get_request_config(cls, request, request_method):\n        try:\n            from . import APP_CONFIG_KEY\n\n            cors = request.app[APP_CONFIG_KEY]\n        except KeyError:\n            raise ValueError(\"aiohttp-cors is not configured.\")\n\n        method = getattr(cls, request_method.lower(), None)\n\n        if not method:\n            raise KeyError()\n\n        config_property_key = f\"{request_method.lower()}_cors_config\"\n\n        custom_config = getattr(method, config_property_key, None)\n        if not custom_config:\n            custom_config = {}\n\n        class_config = cls.cors_config\n        if not class_config:\n            class_config = {}\n\n        return collections.ChainMap(custom_config, class_config, cors.defaults)\n\n    async def _get_config(self, request, origin, request_method):\n        return self.get_request_config(request, request_method)\n\n    async def options(self):\n        response = await self._preflight_handler(self.request)\n        return response\n"
  },
  {
    "path": "aiohttp_cors/preflight_handler.py",
    "content": "from aiohttp import hdrs, web\n\n# Positive response to Access-Control-Allow-Credentials\n_TRUE = \"true\"\n\n\nclass _PreflightHandler:\n\n    @staticmethod\n    def _parse_request_method(request: web.Request):\n        \"\"\"Parse Access-Control-Request-Method header of the preflight request\"\"\"\n        method = request.headers.get(hdrs.ACCESS_CONTROL_REQUEST_METHOD)\n        if method is None:\n            raise web.HTTPForbidden(\n                text=\"CORS preflight request failed: \"\n                \"'Access-Control-Request-Method' header is not specified\"\n            )\n\n        # FIXME: validate method string (ABNF: method = token), if parsing\n        # fails, raise HTTPForbidden.\n\n        return method\n\n    @staticmethod\n    def _parse_request_headers(request: web.Request):\n        \"\"\"Parse Access-Control-Request-Headers header or the preflight request\n\n        Returns set of headers in upper case.\n        \"\"\"\n        headers = request.headers.get(hdrs.ACCESS_CONTROL_REQUEST_HEADERS)\n        if headers is None:\n            return frozenset()\n\n        # FIXME: validate each header string, if parsing fails, raise\n        # HTTPForbidden.\n        # FIXME: check, that headers split and stripped correctly (according\n        # to ABNF).\n        headers = (h.strip(\" \\t\").upper() for h in headers.split(\",\"))\n        # pylint: disable=bad-builtin\n        return frozenset(filter(None, headers))\n\n    async def _get_config(self, request, origin, request_method):\n        raise NotImplementedError()\n\n    async def _preflight_handler(self, request: web.Request):\n        \"\"\"CORS preflight request handler\"\"\"\n\n        # Handle according to part 6.2 of the CORS specification.\n\n        origin = request.headers.get(hdrs.ORIGIN)\n        if origin is None:\n            # Terminate CORS according to CORS 6.2.1.\n            raise web.HTTPForbidden(\n                text=\"CORS preflight request failed: \"\n                \"origin header is not specified in the request\"\n            )\n\n        # CORS 6.2.3. Doing it out of order is not an error.\n        request_method = self._parse_request_method(request)\n\n        # CORS 6.2.5. Doing it out of order is not an error.\n\n        try:\n            config = await self._get_config(request, origin, request_method)\n        except KeyError:\n            raise web.HTTPForbidden(\n                text=\"CORS preflight request failed: \"\n                \"request method {!r} is not allowed \"\n                \"for {!r} origin\".format(request_method, origin)\n            )\n\n        if not config:\n            # No allowed origins for the route.\n            # Terminate CORS according to CORS 6.2.1.\n            raise web.HTTPForbidden(\n                text=\"CORS preflight request failed: \" \"no origins are allowed\"\n            )\n\n        options = config.get(origin, config.get(\"*\"))\n        if options is None:\n            # No configuration for the origin - deny.\n            # Terminate CORS according to CORS 6.2.2.\n            raise web.HTTPForbidden(\n                text=\"CORS preflight request failed: \"\n                \"origin '{}' is not allowed\".format(origin)\n            )\n\n        # CORS 6.2.4\n        request_headers = self._parse_request_headers(request)\n\n        # CORS 6.2.6\n        if options.allow_headers == \"*\":\n            pass\n        else:\n            disallowed_headers = request_headers - options.allow_headers\n            if disallowed_headers:\n                raise web.HTTPForbidden(\n                    text=\"CORS preflight request failed: \"\n                    \"headers are not allowed: {}\".format(\", \".join(disallowed_headers))\n                )\n\n        # Ok, CORS actual request with specified in the preflight request\n        # parameters is allowed.\n        # Set appropriate headers and return 200 response.\n\n        response = web.Response()\n\n        # CORS 6.2.7\n        response.headers[hdrs.ACCESS_CONTROL_ALLOW_ORIGIN] = origin\n        if options.allow_credentials:\n            # Set allowed credentials.\n            response.headers[hdrs.ACCESS_CONTROL_ALLOW_CREDENTIALS] = _TRUE\n\n        # CORS 6.2.8\n        if options.max_age is not None:\n            response.headers[hdrs.ACCESS_CONTROL_MAX_AGE] = str(options.max_age)\n\n        # CORS 6.2.9\n        # TODO: more optimal for client preflight request cache would be to\n        # respond with ALL allowed methods.\n        response.headers[hdrs.ACCESS_CONTROL_ALLOW_METHODS] = request_method\n\n        # CORS 6.2.10\n        if request_headers:\n            # Note: case of the headers in the request is changed, but this\n            # shouldn't be a problem, since the headers should be compared in\n            # the case-insensitive way.\n            response.headers[hdrs.ACCESS_CONTROL_ALLOW_HEADERS] = \",\".join(\n                request_headers\n            )\n\n        return response\n"
  },
  {
    "path": "aiohttp_cors/py.typed",
    "content": ""
  },
  {
    "path": "aiohttp_cors/resource_options.py",
    "content": "# Copyright 2015 Vladimir Rutsky <vladimir@rutsky.org>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Resource CORS options class definition.\"\"\"\n\nimport collections\nimport collections.abc\nimport numbers\n\n__all__ = (\"ResourceOptions\",)\n\n\ndef _is_proper_sequence(seq):\n    \"\"\"Returns is seq is sequence and not string.\"\"\"\n    return isinstance(seq, collections.abc.Sequence) and not isinstance(seq, str)\n\n\nclass ResourceOptions(\n    collections.namedtuple(\n        \"Base\",\n        (\n            \"allow_credentials\",\n            \"expose_headers\",\n            \"allow_headers\",\n            \"max_age\",\n            \"allow_methods\",\n        ),\n    )\n):\n    \"\"\"Resource CORS options.\"\"\"\n\n    __slots__ = ()\n\n    def __init__(\n        self,\n        *,\n        allow_credentials=False,\n        expose_headers=(),\n        allow_headers=(),\n        max_age=None,\n        allow_methods=None\n    ):\n        \"\"\"Construct resource CORS options.\n\n        Options will be normalized.\n\n        :param allow_credentials:\n            Is passing client credentials to the resource from other origin\n            is allowed.\n            See <http://www.w3.org/TR/cors/#user-credentials> for\n            the definition.\n        :type allow_credentials: bool\n            Is passing client credentials to the resource from other origin\n            is allowed.\n\n        :param expose_headers:\n            Server headers that are allowed to be exposed to the client.\n            Simple response headers are excluded from this set, see\n            <http://www.w3.org/TR/cors/#list-of-exposed-headers>.\n        :type expose_headers: sequence of strings or ``*`` string.\n\n        :param allow_headers:\n            Client headers that are allowed to be passed to the resource.\n            See <http://www.w3.org/TR/cors/#list-of-headers>.\n        :type allow_headers: sequence of strings or ``*`` string.\n\n        :param max_age:\n            How long the results of a preflight request can be cached in a\n            preflight result cache (in seconds).\n            See <http://www.w3.org/TR/cors/#http-access-control-max-age>.\n\n        :param allow_methods:\n            List of allowed methods or ``*``string. Can be used in resource or\n            global defaults, but not in specific route.\n\n            It's not required to specify all allowed methods for specific\n            resource, routes that have explicit CORS configuration will be\n            treated as if their methods are allowed.\n        \"\"\"\n        super().__init__()\n\n    def __new__(\n        cls,\n        *,\n        allow_credentials=False,\n        expose_headers=(),\n        allow_headers=(),\n        max_age=None,\n        allow_methods=None\n    ):\n        \"\"\"Normalize source parameters and store them in namedtuple.\"\"\"\n\n        if not isinstance(allow_credentials, bool):\n            raise ValueError(\n                \"'allow_credentials' must be boolean, \"\n                \"got '{!r}'\".format(allow_credentials)\n            )\n        _allow_credentials = allow_credentials\n\n        # `expose_headers` is either \"*\", or sequence of strings.\n        if expose_headers == \"*\":\n            _expose_headers = expose_headers\n        elif not _is_proper_sequence(expose_headers):\n            raise ValueError(\n                \"'expose_headers' must be either '*', or sequence of strings, \"\n                \"got '{!r}'\".format(expose_headers)\n            )\n        elif expose_headers:\n            # \"Access-Control-Expose-Headers\" \":\" #field-name\n            # TODO: Check that headers are valid.\n            # TODO: Remove headers that in the _SIMPLE_RESPONSE_HEADERS set\n            # according to\n            # <http://www.w3.org/TR/cors/#list-of-exposed-headers>.\n            _expose_headers = frozenset(expose_headers)\n        else:\n            # No headers exposed.\n            _expose_headers = frozenset()\n\n        # `allow_headers` is either \"*\", or set of headers in upper case.\n        if allow_headers == \"*\":\n            _allow_headers = allow_headers\n        elif not _is_proper_sequence(allow_headers):\n            raise ValueError(\n                \"'allow_headers' must be either '*', or sequence of strings, \"\n                \"got '{!r}'\".format(allow_headers)\n            )\n        else:\n            # TODO: Check that headers are valid.\n            _allow_headers = frozenset(h.upper() for h in allow_headers)\n\n        if max_age is None:\n            _max_age = None\n        else:\n            if not isinstance(max_age, numbers.Integral) or max_age < 0:\n                raise ValueError(\n                    \"'max_age' must be non-negative integer, \"\n                    \"got '{!r}'\".format(max_age)\n                )\n            _max_age = max_age\n\n        if allow_methods is None or allow_methods == \"*\":\n            _allow_methods = allow_methods\n        elif not _is_proper_sequence(allow_methods):\n            raise ValueError(\n                \"'allow_methods' must be either '*', or sequence of strings, \"\n                \"got '{!r}'\".format(allow_methods)\n            )\n        else:\n            # TODO: Check that methods are valid.\n            _allow_methods = frozenset(m.upper() for m in allow_methods)\n\n        return super().__new__(\n            cls,\n            allow_credentials=_allow_credentials,\n            expose_headers=_expose_headers,\n            allow_headers=_allow_headers,\n            max_age=_max_age,\n            allow_methods=_allow_methods,\n        )\n\n    def is_method_allowed(self, method):\n        if self.allow_methods is None:\n            return False\n\n        if self.allow_methods == \"*\":\n            return True\n\n        return method.upper() in self.allow_methods\n"
  },
  {
    "path": "aiohttp_cors/urldispatcher_router_adapter.py",
    "content": "# Copyright 2015 Vladimir Rutsky <vladimir@rutsky.org>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"AbstractRouterAdapter for aiohttp.web.UrlDispatcher.\"\"\"\nimport collections\nfrom typing import Union\n\nfrom aiohttp import hdrs, web\n\nfrom .abc import AbstractRouterAdapter\nfrom .mixin import CorsViewMixin\n\n# There several usage patterns of routes which should be handled\n# differently.\n#\n# 1. Using new Resources:\n#\n#     resource = app.router.add_resource(path)\n#     cors.add(resource, resource_defaults=...)\n#     cors.add(resource.add_route(method1, handler1), config=...)\n#     cors.add(resource.add_route(method2, handler2), config=...)\n#     cors.add(resource.add_route(method3, handler3), config=...)\n#\n# Here all related Routes (i.e. routes with the same path) are in\n# a single Resource.\n#\n# 2. Using `router.add_static()`:\n#\n#     route1 = app.router.add_static(\n#         \"/images\", \"/usr/share/app/images/\")\n#     cors.add(route1, config=...)\n#\n# Here old-style `web.StaticRoute` is created and wrapped with\n# `web.ResourceAdapter`.\n#\n# 3. Using old `router.add_route()`:\n#\n#     cors.add(app.router.add_route(method1, path, hand1), config=...)\n#     cors.add(app.router.add_route(method2, path, hand2), config=...)\n#     cors.add(app.router.add_route(method3, path, hand3), config=...)\n#\n# This creates three Resources with single Route in each.\n#\n# 4. Using deprecated `register_route` with manually created\n#    `web.Route`:\n#\n#     route1 = RouteSubclass(...)\n#     app.router.register_route(route1)\n#     cors.add(route1, config=...)\n#\n# Here old-style route is wrapped with `web.ResourceAdapter`.\n#\n# Preflight requests is roughly an OPTIONS request with query\n# \"is specific HTTP method is allowed\".\n# In order to properly handle preflight request we need to know which\n# routes have enabled CORS on the request path and CORS configuration\n# for requested HTTP method.\n#\n# In case of new usage pattern it's simple: we need to take a look at\n# self._resource_config[resource][method] for the processing resource.\n#\n# In case of old usage pattern we need to iterate over routes with\n# enabled CORS and check is requested path and HTTP method is accepted\n# by a route.\n\n\nclass _ResourceConfig:\n    def __init__(self, default_config):\n        # Resource default config.\n        self.default_config = default_config\n\n        # HTTP method to route configuration.\n        self.method_config = {}\n\n\ndef _is_web_view(entity, strict=True):\n    webview = False\n    if isinstance(entity, web.AbstractRoute):\n        handler = entity.handler\n        if isinstance(handler, type) and issubclass(handler, web.View):\n            webview = True\n            if not issubclass(handler, CorsViewMixin):\n                if strict:\n                    raise ValueError(\n                        \"web view should be derived from \"\n                        \"aiohttp_cors.CorsViewMixin for working \"\n                        \"with the library\"\n                    )\n                else:\n                    return False\n    return webview\n\n\nclass ResourcesUrlDispatcherRouterAdapter(AbstractRouterAdapter):\n    \"\"\"Adapter for `UrlDispatcher` for Resources-based routing only.\n\n    Should be used with routes added in the following way:\n\n        resource = app.router.add_resource(path)\n        cors.add(resource, resource_defaults=...)\n        cors.add(resource.add_route(method1, handler1), config=...)\n        cors.add(resource.add_route(method2, handler2), config=...)\n        cors.add(resource.add_route(method3, handler3), config=...)\n    \"\"\"\n\n    def __init__(self, router: web.UrlDispatcher, defaults):\n        \"\"\"\n        :param defaults:\n            Default CORS configuration.\n        \"\"\"\n        self._router = router\n\n        # Default configuration for all routes.\n        self._default_config = defaults\n\n        # Mapping from Resource to _ResourceConfig.\n        self._resource_config = {}\n\n        self._resources_with_preflight_handlers = set()\n        self._preflight_routes = set()\n\n    def add_preflight_handler(\n        self,\n        routing_entity: Union[web.Resource, web.StaticResource, web.ResourceRoute],\n        handler,\n    ):\n        \"\"\"Add OPTIONS handler for all routes defined by `routing_entity`.\n\n        Does nothing if CORS handler already handles routing entity.\n        Should fail if there are conflicting user-defined OPTIONS handlers.\n        \"\"\"\n\n        if isinstance(routing_entity, web.Resource):\n            resource = routing_entity\n\n            # Add preflight handler for Resource, if not yet added.\n\n            if resource in self._resources_with_preflight_handlers:\n                # Preflight handler already added for this resource.\n                return\n            for route_obj in resource:\n                if route_obj.method == hdrs.METH_OPTIONS:\n                    if route_obj.handler is handler:\n                        return  # already added\n                    else:\n                        raise ValueError(\n                            \"{!r} already has OPTIONS handler {!r}\".format(\n                                resource, route_obj.handler\n                            )\n                        )\n                elif route_obj.method == hdrs.METH_ANY:\n                    if _is_web_view(route_obj):\n                        self._preflight_routes.add(route_obj)\n                        self._resources_with_preflight_handlers.add(resource)\n                        return\n                    else:\n                        raise ValueError(\n                            \"{!r} already has a '*' handler \"\n                            \"for all methods\".format(resource)\n                        )\n\n            preflight_route = resource.add_route(hdrs.METH_OPTIONS, handler)\n            self._preflight_routes.add(preflight_route)\n            self._resources_with_preflight_handlers.add(resource)\n\n        elif isinstance(routing_entity, web.StaticResource):\n            resource = routing_entity\n\n            # Add preflight handler for Resource, if not yet added.\n\n            if resource in self._resources_with_preflight_handlers:\n                # Preflight handler already added for this resource.\n                return\n\n            resource.set_options_route(handler)\n            preflight_route = resource._routes[hdrs.METH_OPTIONS]\n            self._preflight_routes.add(preflight_route)\n            self._resources_with_preflight_handlers.add(resource)\n\n        elif isinstance(routing_entity, web.ResourceRoute):\n            route = routing_entity\n\n            if not self.is_cors_for_resource(route.resource):\n                self.add_preflight_handler(route.resource, handler)\n\n        else:\n            raise ValueError(\n                f\"Resource or ResourceRoute expected, got {routing_entity!r}\"\n            )\n\n    def is_cors_for_resource(self, resource: web.Resource) -> bool:\n        \"\"\"Is CORS is configured for the resource\"\"\"\n        return resource in self._resources_with_preflight_handlers\n\n    def _request_route(self, request: web.Request) -> web.ResourceRoute:\n        match_info = request.match_info\n        assert isinstance(match_info, web.UrlMappingMatchInfo)\n        return match_info.route\n\n    def _request_resource(self, request: web.Request) -> web.Resource:\n        return self._request_route(request).resource\n\n    def is_preflight_request(self, request: web.Request) -> bool:\n        \"\"\"Is `request` is a CORS preflight request.\"\"\"\n        route = self._request_route(request)\n        if _is_web_view(route, strict=False):\n            return request.method == \"OPTIONS\"\n        return route in self._preflight_routes\n\n    def is_cors_enabled_on_request(self, request: web.Request) -> bool:\n        \"\"\"Is `request` is a request for CORS-enabled resource.\"\"\"\n\n        return self._request_resource(request) in self._resource_config\n\n    def set_config_for_routing_entity(\n        self,\n        routing_entity: Union[web.Resource, web.StaticResource, web.ResourceRoute],\n        config,\n    ):\n        \"\"\"Record configuration for resource or it's route.\"\"\"\n\n        if isinstance(routing_entity, (web.Resource, web.StaticResource)):\n            resource = routing_entity\n\n            # Add resource configuration or fail if it's already added.\n            if resource in self._resource_config:\n                raise ValueError(\n                    f\"CORS is already configured for {resource!r} resource.\"\n                )\n\n            self._resource_config[resource] = _ResourceConfig(default_config=config)\n\n        elif isinstance(routing_entity, web.ResourceRoute):\n            route = routing_entity\n\n            # Add resource's route configuration or fail if it's already added.\n            if route.resource not in self._resource_config:\n                self.set_config_for_routing_entity(route.resource, config)\n\n            if route.resource not in self._resource_config:\n                raise ValueError(\n                    \"Can't setup CORS for {!r} request, \"\n                    \"CORS must be enabled for route's resource first.\".format(route)\n                )\n\n            resource_config = self._resource_config[route.resource]\n\n            if route.method in resource_config.method_config:\n                raise ValueError(\n                    \"Can't setup CORS for {!r} route: CORS already \"\n                    \"configured on resource {!r} for {} method\".format(\n                        route, route.resource, route.method\n                    )\n                )\n\n            resource_config.method_config[route.method] = config\n\n        else:\n            raise ValueError(\n                f\"Resource or ResourceRoute expected, got {routing_entity!r}\"\n            )\n\n    async def get_preflight_request_config(\n        self, preflight_request: web.Request, origin: str, requested_method: str\n    ):\n        assert self.is_preflight_request(preflight_request)\n\n        resource = self._request_resource(preflight_request)\n        resource_config = self._resource_config[resource]\n        defaulted_config = collections.ChainMap(\n            resource_config.default_config, self._default_config\n        )\n\n        options = defaulted_config.get(origin, defaulted_config.get(\"*\"))\n        if options is not None and options.is_method_allowed(requested_method):\n            # Requested method enabled for CORS in defaults, override it with\n            # explicit route configuration (if any).\n            route_config = resource_config.method_config.get(requested_method, {})\n\n        else:\n            # Requested method is not enabled in defaults.\n            # Enable CORS for it only if explicit configuration exists.\n            route_config = resource_config.method_config[requested_method]\n\n        defaulted_config = collections.ChainMap(route_config, defaulted_config)\n\n        return defaulted_config\n\n    def get_non_preflight_request_config(self, request: web.Request):\n        \"\"\"Get stored CORS configuration for routing entity that handles\n        specified request.\"\"\"\n\n        assert self.is_cors_enabled_on_request(request)\n\n        resource = self._request_resource(request)\n        resource_config = self._resource_config[resource]\n        # Take Route config (if any) with defaults from Resource CORS\n        # configuration and global defaults.\n        route = request.match_info.route\n        if _is_web_view(route, strict=False):\n            method_config = request.match_info.handler.get_request_config(\n                request, request.method\n            )\n        else:\n            method_config = resource_config.method_config.get(request.method, {})\n        defaulted_config = collections.ChainMap(\n            method_config, resource_config.default_config, self._default_config\n        )\n\n        return defaulted_config\n"
  },
  {
    "path": "install_python_and_pip.ps1",
    "content": "# Sample script to install Python and pip under Windows\n# Authors: Olivier Grisel and Kyle Kastner\n# License: BSD 3 clause\n\n# The script is borrowed from Scikit-learn project\n\n$BASE_URL = \"https://www.python.org/ftp/python/\"\n$GET_PIP_URL = \"https://bootstrap.pypa.io/get-pip.py\"\n$GET_PIP_PATH = \"C:\\get-pip.py\"\n\n\nfunction DownloadPython ($python_version, $platform_suffix) {\n    $webclient = New-Object System.Net.WebClient\n    $filename = \"python-\" + $python_version + $platform_suffix + \".msi\"\n    $url = $BASE_URL + $python_version + \"/\" + $filename\n\n    $basedir = $pwd.Path + \"\\\"\n    $filepath = $basedir + $filename\n    if (Test-Path $filename) {\n        Write-Host \"Reusing\" $filepath\n        return $filepath\n    }\n\n    # Download and retry up to 3 times in case of network transient errors.\n    Write-Host \"Downloading\" $filename \"from\" $url\n    $retry_attempts = 2\n    for($i=0; $i -lt $retry_attempts; $i++){\n        try {\n            $webclient.DownloadFile($url, $filepath)\n            break\n        }\n        Catch [Exception]{\n            Start-Sleep 1\n        }\n   }\n   if (Test-Path $filepath) {\n       Write-Host \"File saved at\" $filepath\n   } else {\n       # Retry once to get the error message if any at the last try\n       $webclient.DownloadFile($url, $filepath)\n   }\n   return $filepath\n}\n\n\nfunction InstallPython ($python_version, $architecture, $python_home) {\n    Write-Host \"Installing Python\" $python_version \"for\" $architecture \"bit architecture to\" $python_home\n    if (Test-Path $python_home) {\n        Write-Host $python_home \"already exists, skipping.\"\n        return $false\n    }\n    if ($architecture -eq \"32\") {\n        $platform_suffix = \"\"\n    } else {\n        $platform_suffix = \".amd64\"\n    }\n    $msipath = DownloadPython $python_version $platform_suffix\n    Write-Host \"Installing\" $msipath \"to\" $python_home\n    $install_log = $python_home + \".log\"\n    $install_args = \"/qn /log $install_log /i $msipath TARGETDIR=$python_home\"\n    $uninstall_args = \"/qn /x $msipath\"\n    RunCommand \"msiexec.exe\" $install_args\n    if (-not(Test-Path $python_home)) {\n        Write-Host \"Python seems to be installed else-where, reinstalling.\"\n        RunCommand \"msiexec.exe\" $uninstall_args\n        RunCommand \"msiexec.exe\" $install_args\n    }\n    if (Test-Path $python_home) {\n        Write-Host \"Python $python_version ($architecture) installation complete\"\n    } else {\n        Write-Host \"Failed to install Python in $python_home\"\n        Get-Content -Path $install_log\n        Exit 1\n    }\n}\n\nfunction RunCommand ($command, $command_args) {\n    Write-Host $command $command_args\n    Start-Process -FilePath $command -ArgumentList $command_args -Wait -Passthru\n}\n\n\nfunction InstallPip ($python_home) {\n    $pip_path = $python_home + \"\\Scripts\\pip.exe\"\n    $python_path = $python_home + \"\\python.exe\"\n    if (-not(Test-Path $pip_path)) {\n        Write-Host \"Installing pip...\"\n        $webclient = New-Object System.Net.WebClient\n        $webclient.DownloadFile($GET_PIP_URL, $GET_PIP_PATH)\n        Write-Host \"Executing:\" $python_path $GET_PIP_PATH\n        Start-Process -FilePath \"$python_path\" -ArgumentList \"$GET_PIP_PATH\" -Wait -Passthru\n    } else {\n        Write-Host \"pip already installed.\"\n    }\n}\n\n\nfunction main () {\n    InstallPython $env:PYTHON_VERSION $env:PYTHON_ARCH $env:PYTHON\n    InstallPip $env:PYTHON\n}\n\nmain\n"
  },
  {
    "path": "pytest.ini",
    "content": "[pytest]\nfilterwarnings=\n    error\n"
  },
  {
    "path": "requirements-dev.txt",
    "content": "-e .\naiohttp==3.13.5\npackaging==26.2\npytest==8.4.2\npytest-aiohttp==0.3.0\npytest-cov==7.1.0\nselenium==4.34.0\ntox==4.30.3\nurllib3==2.4.0  # for selenium\n"
  },
  {
    "path": "setup.cfg",
    "content": "[tool:pytest]\naddopts= --cov=aiohttp_cors --cov-report=term --cov-report=html --cov-branch --no-cov-on-fail\n\n[flake8]\nextend-select =\n  B950,\n  # NIC001 -- \"Implicitly concatenated str literals on one line\"\n  NIC001,\n  # NIC101 -- \"Implicitly concatenated bytes literals on one line\"\n  NIC101,\n# TODO: don't disable D*, fix up issues instead\nignore = N801,N802,N803,NIC002,NIC102,E203,E226,E305,W504,E252,E301,E302,E501,E704,W503,W504,D1,D4\nmax-line-length = 88\n# flake8-requirements\nrequirements-file = requirements-dev.txt\nrequirements-max-depth = 4\n\n[isort]\nline_length=88\ninclude_trailing_comma=True\nmulti_line_output=3\nforce_grid_wrap=0\ncombine_as_imports=True\n\nknown_third_party=pytest,multidict,yarl\nknown_first_party=aiohttp\n"
  },
  {
    "path": "setup.py",
    "content": "# Copyright 2015 Vladimir Rutsky <vladimir@rutsky.org>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport os\nimport sys\n\nfrom setuptools import setup\n\n\ndef read_file(filename):\n    abs_path = os.path.join(os.path.dirname(__file__), filename)\n    with open(abs_path, encoding=\"utf-8\") as f:\n        return f.read()\n\n\nabout = {}\nexec(read_file(os.path.join(\"aiohttp_cors\", \"__about__.py\")), about)\n\nneeds_pytest = {\"pytest\", \"test\"}.intersection(sys.argv)\npytest_runner = [\"pytest_runner\"] if needs_pytest else []\n\n\nsetup(\n    name=about[\"__title__\"],\n    version=about[\"__version__\"],\n    author=about[\"__author__\"],\n    author_email=about[\"__email__\"],\n    description=about[\"__summary__\"],\n    url=about[\"__uri__\"],\n    long_description=\"\\n\\n\".join(\n        (\n            read_file(\"README.rst\"),\n            read_file(\"CHANGES.rst\"),\n        )\n    ),\n    long_description_content_type=\"text/x-rst\",\n    packages=[\"aiohttp_cors\"],\n    setup_requires=[\n        # Environment markers were implemented and stabilized in setuptools\n        # v20.8.1 (see <http://stackoverflow.com/a/32643122/391865>).\n        \"setuptools>=20.8.1\",\n        # If line above doesn't work, check that you have at least\n        # setuptools v19.4 (released 2016-01-16):\n        # <https://github.com/pypa/setuptools/issues/141>\n    ]\n    + pytest_runner,\n    tests_require=[\n        \"pytest\",\n        \"pytest-cov\",\n        \"pytest-pylint\",\n        \"selenium\",\n    ],\n    test_suite=\"tests\",\n    install_requires=[\n        \"aiohttp>=3.9\",\n    ],\n    python_requires=\">=3.9\",\n    license=about[\"__license__\"],\n    classifiers=[\n        \"License :: OSI Approved :: Apache Software License\",\n        \"Intended Audience :: Developers\",\n        \"Programming Language :: Python\",\n        \"Programming Language :: Python :: 3\",\n        \"Topic :: Software Development :: Libraries\",\n        \"Topic :: Internet :: WWW/HTTP\",\n        \"Framework :: AsyncIO\",\n        \"Operating System :: MacOS :: MacOS X\",\n        \"Operating System :: Microsoft :: Windows\",\n        \"Operating System :: POSIX\",\n        \"Development Status :: 3 - Alpha\",\n        \"Framework :: aiohttp\",\n    ],\n)\n"
  },
  {
    "path": "tests/__init__.py",
    "content": ""
  },
  {
    "path": "tests/doc/__init__.py",
    "content": ""
  },
  {
    "path": "tests/doc/test_basic_usage.py",
    "content": "# Copyright 2015 Vladimir Rutsky <vladimir@rutsky.org>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Test basic usage.\"\"\"\n\n\nasync def test_main():\n    # This tests corresponds to example from documentation.\n    # If you updating it, don't forget to update documentation.\n\n    import aiohttp_cors\n    from aiohttp import web\n\n    async def handler(request):\n        return web.Response(\n            text=\"Hello!\",\n            headers={\n                \"X-Custom-Server-Header\": \"Custom data\",\n            },\n        )\n\n    app = web.Application()\n\n    # `aiohttp_cors.setup` returns `aiohttp_cors.CorsConfig` instance.\n    # The `cors` instance will store CORS configuration for the\n    # application.\n    cors = aiohttp_cors.setup(app)\n\n    # To enable CORS processing for specific route you need to add\n    # that route to the CORS configuration object and specify its\n    # CORS options.\n    resource = cors.add(app.router.add_resource(\"/hello\"))\n    route = cors.add(\n        resource.add_route(\"GET\", handler),\n        {\n            \"http://client.example.org\": aiohttp_cors.ResourceOptions(\n                allow_credentials=True,\n                expose_headers=(\"X-Custom-Server-Header\",),\n                allow_headers=(\"X-Requested-With\", \"Content-Type\"),\n                max_age=3600,\n            )\n        },\n    )\n\n    assert route is not None\n\n\nasync def test_defaults():\n    # This tests corresponds to example from documentation.\n    # If you updating it, don't forget to update documentation.\n\n    import aiohttp_cors\n    from aiohttp import web\n\n    async def handler(request):\n        return web.Response(\n            text=\"Hello!\",\n            headers={\n                \"X-Custom-Server-Header\": \"Custom data\",\n            },\n        )\n\n    handler_post = handler\n    handler_put = handler\n\n    app = web.Application()\n\n    # Example:\n\n    cors = aiohttp_cors.setup(\n        app,\n        defaults={\n            # Allow all to read all CORS-enabled resources from\n            # http://client.example.org.\n            \"http://client.example.org\": aiohttp_cors.ResourceOptions(),\n        },\n    )\n\n    # Enable CORS on routes.\n\n    # According to defaults POST and PUT will be available only to\n    # \"http://client.example.org\".\n    hello_resource = cors.add(app.router.add_resource(\"/hello\"))\n    cors.add(hello_resource.add_route(\"POST\", handler_post))\n    cors.add(hello_resource.add_route(\"PUT\", handler_put))\n\n    # In addition to \"http://client.example.org\", GET request will be\n    # allowed from \"http://other-client.example.org\" origin.\n    cors.add(\n        hello_resource.add_route(\"GET\", handler),\n        {\n            \"http://other-client.example.org\": aiohttp_cors.ResourceOptions(),\n        },\n    )\n\n    # CORS will be enabled only on the resources added to `CorsConfig`,\n    # so following resource will be NOT CORS-enabled.\n    app.router.add_route(\"GET\", \"/private\", handler)\n"
  },
  {
    "path": "tests/integration/__init__.py",
    "content": ""
  },
  {
    "path": "tests/integration/test_main.py",
    "content": "# Copyright 2015 Vladimir Rutsky <vladimir@rutsky.org>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Test generic usage\"\"\"\n\nimport pathlib\n\nimport pytest\n\nfrom aiohttp import hdrs, web\nfrom aiohttp_cors import CorsViewMixin, ResourceOptions, setup as _setup\n\nTEST_BODY = \"Hello, world\"\nSERVER_CUSTOM_HEADER_NAME = \"X-Server-Custom-Header\"\nSERVER_CUSTOM_HEADER_VALUE = \"some value\"\n\n\n# pylint: disable=unused-argument\nasync def handler(request: web.Request) -> web.StreamResponse:\n    \"\"\"Dummy request handler, returning `TEST_BODY`.\"\"\"\n    response = web.Response(text=TEST_BODY)\n\n    response.headers[SERVER_CUSTOM_HEADER_NAME] = SERVER_CUSTOM_HEADER_VALUE\n\n    return response\n\n\nclass WebViewHandler(web.View, CorsViewMixin):\n\n    async def get(self) -> web.StreamResponse:\n        \"\"\"Dummy request handler, returning `TEST_BODY`.\"\"\"\n        response = web.Response(text=TEST_BODY)\n\n        response.headers[SERVER_CUSTOM_HEADER_NAME] = SERVER_CUSTOM_HEADER_VALUE\n\n        return response\n\n\n@pytest.fixture(params=[\"resource\", \"view\", \"route\"])\ndef make_app(request):\n    def inner(defaults, route_config):\n        app = web.Application()\n        cors = _setup(app, defaults=defaults)\n\n        if request.param == \"resource\":\n            resource = cors.add(app.router.add_resource(\"/resource\"))\n            cors.add(resource.add_route(\"GET\", handler), route_config)\n        elif request.param == \"view\":\n            WebViewHandler.cors_config = route_config\n            cors.add(app.router.add_route(\"*\", \"/resource\", WebViewHandler))\n        elif request.param == \"route\":\n            cors.add(app.router.add_route(\"GET\", \"/resource\", handler), route_config)\n        else:\n            raise RuntimeError(f\"unknown parameter {request.param}\")\n\n        return app\n\n    return inner\n\n\nasync def test_message_roundtrip(aiohttp_client):\n    \"\"\"Test that aiohttp server is correctly setup in the base class.\"\"\"\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/\", handler)\n\n    client = await aiohttp_client(app)\n\n    resp = await client.get(\"/\")\n    assert resp.status == 200\n    data = await resp.text()\n\n    assert data == TEST_BODY\n\n\nasync def test_dummy_setup(aiohttp_server):\n    \"\"\"Test a dummy configuration.\"\"\"\n    app = web.Application()\n    _setup(app)\n\n    await aiohttp_server(app)\n\n\nasync def test_dummy_setup_roundtrip(aiohttp_client):\n    \"\"\"Test a dummy configuration with a message round-trip.\"\"\"\n    app = web.Application()\n    _setup(app)\n\n    app.router.add_route(\"GET\", \"/\", handler)\n\n    client = await aiohttp_client(app)\n\n    resp = await client.get(\"/\")\n    assert resp.status == 200\n    data = await resp.text()\n\n    assert data == TEST_BODY\n\n\nasync def test_dummy_setup_roundtrip_resource(aiohttp_client):\n    \"\"\"Test a dummy configuration with a message round-trip.\"\"\"\n    app = web.Application()\n    _setup(app)\n\n    app.router.add_resource(\"/\").add_route(\"GET\", handler)\n\n    client = await aiohttp_client(app)\n\n    resp = await client.get(\"/\")\n    assert resp.status == 200\n    data = await resp.text()\n\n    assert data == TEST_BODY\n\n\nasync def test_simple_no_origin(aiohttp_client, make_app):\n    app = make_app(None, {\"http://client1.example.org\": ResourceOptions()})\n\n    client = await aiohttp_client(app)\n\n    resp = await client.get(\"/resource\")\n    assert resp.status == 200\n    resp_text = await resp.text()\n    assert resp_text == TEST_BODY\n\n    for header_name in {\n        hdrs.ACCESS_CONTROL_ALLOW_ORIGIN,\n        hdrs.ACCESS_CONTROL_EXPOSE_HEADERS,\n        hdrs.ACCESS_CONTROL_ALLOW_CREDENTIALS,\n    }:\n        assert header_name not in resp.headers\n\n\nasync def test_simple_allowed_origin(aiohttp_client, make_app):\n    app = make_app(None, {\"http://client1.example.org\": ResourceOptions()})\n\n    client = await aiohttp_client(app)\n\n    resp = await client.get(\n        \"/resource\", headers={hdrs.ORIGIN: \"http://client1.example.org\"}\n    )\n    assert resp.status == 200\n    resp_text = await resp.text()\n    assert resp_text == TEST_BODY\n\n    for hdr, val in {\n        hdrs.ACCESS_CONTROL_ALLOW_ORIGIN: \"http://client1.example.org\",\n    }.items():\n        assert resp.headers.get(hdr) == val\n\n    for header_name in {\n        hdrs.ACCESS_CONTROL_EXPOSE_HEADERS,\n        hdrs.ACCESS_CONTROL_ALLOW_CREDENTIALS,\n    }:\n        assert header_name not in resp.headers\n\n\nasync def test_simple_not_allowed_origin(aiohttp_client, make_app):\n    app = make_app(None, {\"http://client1.example.org\": ResourceOptions()})\n\n    client = await aiohttp_client(app)\n\n    resp = await client.get(\n        \"/resource\", headers={hdrs.ORIGIN: \"http://client2.example.org\"}\n    )\n    assert resp.status == 200\n    resp_text = await resp.text()\n    assert resp_text == TEST_BODY\n\n    for header_name in {\n        hdrs.ACCESS_CONTROL_ALLOW_ORIGIN,\n        hdrs.ACCESS_CONTROL_EXPOSE_HEADERS,\n        hdrs.ACCESS_CONTROL_ALLOW_CREDENTIALS,\n    }:\n        assert header_name not in resp.headers\n\n\nasync def test_simple_explicit_port(aiohttp_client, make_app):\n    app = make_app(None, {\"http://client1.example.org\": ResourceOptions()})\n\n    client = await aiohttp_client(app)\n\n    resp = await client.get(\n        \"/resource\", headers={hdrs.ORIGIN: \"http://client1.example.org:80\"}\n    )\n    assert resp.status == 200\n    resp_text = await resp.text()\n    assert resp_text == TEST_BODY\n\n    for header_name in {\n        hdrs.ACCESS_CONTROL_ALLOW_ORIGIN,\n        hdrs.ACCESS_CONTROL_EXPOSE_HEADERS,\n        hdrs.ACCESS_CONTROL_ALLOW_CREDENTIALS,\n    }:\n        assert header_name not in resp.headers\n\n\nasync def test_simple_different_scheme(aiohttp_client, make_app):\n    app = make_app(None, {\"http://client1.example.org\": ResourceOptions()})\n\n    client = await aiohttp_client(app)\n\n    resp = await client.get(\n        \"/resource\", headers={hdrs.ORIGIN: \"https://client1.example.org\"}\n    )\n    assert resp.status == 200\n    resp_text = await resp.text()\n    assert resp_text == TEST_BODY\n\n    for header_name in {\n        hdrs.ACCESS_CONTROL_ALLOW_ORIGIN,\n        hdrs.ACCESS_CONTROL_EXPOSE_HEADERS,\n        hdrs.ACCESS_CONTROL_ALLOW_CREDENTIALS,\n    }:\n        assert header_name not in resp.headers\n\n\n@pytest.fixture(\n    params=[\n        (None, {\"http://client1.example.org\": ResourceOptions(allow_credentials=True)}),\n        ({\"http://client1.example.org\": ResourceOptions(allow_credentials=True)}, None),\n    ]\n)\ndef app_for_credentials(make_app, request):\n    return make_app(*request.param)\n\n\nasync def test_cred_no_origin(aiohttp_client, app_for_credentials):\n    app = app_for_credentials\n\n    client = await aiohttp_client(app)\n\n    resp = await client.get(\"/resource\")\n    assert resp.status == 200\n    resp_text = await resp.text()\n    assert resp_text == TEST_BODY\n\n    for header_name in {\n        hdrs.ACCESS_CONTROL_ALLOW_ORIGIN,\n        hdrs.ACCESS_CONTROL_EXPOSE_HEADERS,\n        hdrs.ACCESS_CONTROL_ALLOW_CREDENTIALS,\n    }:\n        assert header_name not in resp.headers\n\n\nasync def test_cred_allowed_origin(aiohttp_client, app_for_credentials):\n    app = app_for_credentials\n\n    client = await aiohttp_client(app)\n\n    resp = await client.get(\n        \"/resource\", headers={hdrs.ORIGIN: \"http://client1.example.org\"}\n    )\n    assert resp.status == 200\n    resp_text = await resp.text()\n    assert resp_text == TEST_BODY\n\n    for hdr, val in {\n        hdrs.ACCESS_CONTROL_ALLOW_ORIGIN: \"http://client1.example.org\",\n        hdrs.ACCESS_CONTROL_ALLOW_CREDENTIALS: \"true\",\n    }.items():\n        assert resp.headers.get(hdr) == val\n\n    for header_name in {\n        hdrs.ACCESS_CONTROL_EXPOSE_HEADERS,\n    }:\n        assert header_name not in resp.headers\n\n\nasync def test_cred_disallowed_origin(aiohttp_client, app_for_credentials):\n    app = app_for_credentials\n\n    client = await aiohttp_client(app)\n\n    resp = await client.get(\n        \"/resource\", headers={hdrs.ORIGIN: \"http://client2.example.org\"}\n    )\n    assert resp.status == 200\n    resp_text = await resp.text()\n    assert resp_text == TEST_BODY\n\n    for header_name in {\n        hdrs.ACCESS_CONTROL_ALLOW_ORIGIN,\n        hdrs.ACCESS_CONTROL_EXPOSE_HEADERS,\n        hdrs.ACCESS_CONTROL_ALLOW_CREDENTIALS,\n    }:\n        assert header_name not in resp.headers\n\n\nasync def test_simple_expose_headers_no_origin(aiohttp_client, make_app):\n    app = make_app(\n        None,\n        {\n            \"http://client1.example.org\": ResourceOptions(\n                expose_headers=(SERVER_CUSTOM_HEADER_NAME,)\n            )\n        },\n    )\n\n    client = await aiohttp_client(app)\n\n    resp = await client.get(\"/resource\")\n    assert resp.status == 200\n    resp_text = await resp.text()\n    assert resp_text == TEST_BODY\n\n    for header_name in {\n        hdrs.ACCESS_CONTROL_ALLOW_ORIGIN,\n        hdrs.ACCESS_CONTROL_EXPOSE_HEADERS,\n        hdrs.ACCESS_CONTROL_ALLOW_CREDENTIALS,\n    }:\n        assert header_name not in resp.headers\n\n\nasync def test_simple_expose_headers_allowed_origin(aiohttp_client, make_app):\n    app = make_app(\n        None,\n        {\n            \"http://client1.example.org\": ResourceOptions(\n                expose_headers=(SERVER_CUSTOM_HEADER_NAME,)\n            )\n        },\n    )\n\n    client = await aiohttp_client(app)\n\n    resp = await client.get(\n        \"/resource\", headers={hdrs.ORIGIN: \"http://client1.example.org\"}\n    )\n    assert resp.status == 200\n    resp_text = await resp.text()\n    assert resp_text == TEST_BODY\n\n    for hdr, val in {\n        hdrs.ACCESS_CONTROL_ALLOW_ORIGIN: \"http://client1.example.org\",\n        hdrs.ACCESS_CONTROL_EXPOSE_HEADERS: SERVER_CUSTOM_HEADER_NAME,\n    }.items():\n        assert resp.headers.get(hdr) == val\n\n    for header_name in {\n        hdrs.ACCESS_CONTROL_ALLOW_CREDENTIALS,\n    }:\n        assert header_name not in resp.headers\n\n\nasync def test_simple_expose_headers_not_allowed_origin(aiohttp_client, make_app):\n    app = make_app(\n        None,\n        {\n            \"http://client1.example.org\": ResourceOptions(\n                expose_headers=(SERVER_CUSTOM_HEADER_NAME,)\n            )\n        },\n    )\n\n    client = await aiohttp_client(app)\n\n    resp = await client.get(\n        \"/resource\", headers={hdrs.ORIGIN: \"http://client2.example.org\"}\n    )\n    assert resp.status == 200\n    resp_text = await resp.text()\n    assert resp_text == TEST_BODY\n\n    for header_name in {\n        hdrs.ACCESS_CONTROL_ALLOW_ORIGIN,\n        hdrs.ACCESS_CONTROL_EXPOSE_HEADERS,\n        hdrs.ACCESS_CONTROL_ALLOW_CREDENTIALS,\n    }:\n        assert header_name not in resp.headers\n\n\nasync def test_preflight_default_no_origin(aiohttp_client, make_app):\n    app = make_app(None, {\"http://client1.example.org\": ResourceOptions()})\n\n    client = await aiohttp_client(app)\n\n    resp = await client.options(\"/resource\")\n    assert resp.status == 403\n    resp_text = await resp.text()\n    assert \"origin header is not specified\" in resp_text\n\n    for header_name in {\n        hdrs.ACCESS_CONTROL_ALLOW_ORIGIN,\n        hdrs.ACCESS_CONTROL_ALLOW_CREDENTIALS,\n        hdrs.ACCESS_CONTROL_MAX_AGE,\n        hdrs.ACCESS_CONTROL_EXPOSE_HEADERS,\n        hdrs.ACCESS_CONTROL_ALLOW_METHODS,\n        hdrs.ACCESS_CONTROL_ALLOW_HEADERS,\n    }:\n        assert header_name not in resp.headers\n\n\nasync def test_preflight_default_no_method(aiohttp_client, make_app):\n\n    app = make_app(None, {\"http://client1.example.org\": ResourceOptions()})\n\n    client = await aiohttp_client(app)\n\n    resp = await client.options(\n        \"/resource\",\n        headers={\n            hdrs.ORIGIN: \"http://client1.example.org\",\n        },\n    )\n    assert resp.status == 403\n    resp_text = await resp.text()\n    assert \"'Access-Control-Request-Method' header is not specified\" in resp_text\n\n    for header_name in {\n        hdrs.ACCESS_CONTROL_ALLOW_ORIGIN,\n        hdrs.ACCESS_CONTROL_ALLOW_CREDENTIALS,\n        hdrs.ACCESS_CONTROL_MAX_AGE,\n        hdrs.ACCESS_CONTROL_EXPOSE_HEADERS,\n        hdrs.ACCESS_CONTROL_ALLOW_METHODS,\n        hdrs.ACCESS_CONTROL_ALLOW_HEADERS,\n    }:\n        assert header_name not in resp.headers\n\n\nasync def test_preflight_default_origin_and_method(aiohttp_client, make_app):\n\n    app = make_app(None, {\"http://client1.example.org\": ResourceOptions()})\n\n    client = await aiohttp_client(app)\n\n    resp = await client.options(\n        \"/resource\",\n        headers={\n            hdrs.ORIGIN: \"http://client1.example.org\",\n            hdrs.ACCESS_CONTROL_REQUEST_METHOD: \"GET\",\n        },\n    )\n    assert resp.status == 200\n    resp_text = await resp.text()\n    assert \"\" == resp_text\n\n    for hdr, val in {\n        hdrs.ACCESS_CONTROL_ALLOW_ORIGIN: \"http://client1.example.org\",\n        hdrs.ACCESS_CONTROL_ALLOW_METHODS: \"GET\",\n    }.items():\n        assert resp.headers.get(hdr) == val\n\n    for header_name in {\n        hdrs.ACCESS_CONTROL_ALLOW_CREDENTIALS,\n        hdrs.ACCESS_CONTROL_MAX_AGE,\n        hdrs.ACCESS_CONTROL_EXPOSE_HEADERS,\n        hdrs.ACCESS_CONTROL_ALLOW_HEADERS,\n    }:\n        assert header_name not in resp.headers\n\n\nasync def test_preflight_default_disallowed_origin(aiohttp_client, make_app):\n\n    app = make_app(None, {\"http://client1.example.org\": ResourceOptions()})\n\n    client = await aiohttp_client(app)\n\n    resp = await client.options(\n        \"/resource\",\n        headers={\n            hdrs.ORIGIN: \"http://client2.example.org\",\n            hdrs.ACCESS_CONTROL_REQUEST_METHOD: \"GET\",\n        },\n    )\n    assert resp.status == 403\n    resp_text = await resp.text()\n    assert \"origin 'http://client2.example.org' is not allowed\" in resp_text\n\n    for header_name in {\n        hdrs.ACCESS_CONTROL_ALLOW_ORIGIN,\n        hdrs.ACCESS_CONTROL_ALLOW_CREDENTIALS,\n        hdrs.ACCESS_CONTROL_MAX_AGE,\n        hdrs.ACCESS_CONTROL_EXPOSE_HEADERS,\n        hdrs.ACCESS_CONTROL_ALLOW_METHODS,\n        hdrs.ACCESS_CONTROL_ALLOW_HEADERS,\n    }:\n        assert header_name not in resp.headers\n\n\nasync def test_preflight_default_disallowed_method(aiohttp_client, make_app):\n\n    app = make_app(None, {\"http://client1.example.org\": ResourceOptions()})\n\n    client = await aiohttp_client(app)\n\n    resp = await client.options(\n        \"/resource\",\n        headers={\n            hdrs.ORIGIN: \"http://client1.example.org\",\n            hdrs.ACCESS_CONTROL_REQUEST_METHOD: \"POST\",\n        },\n    )\n    assert resp.status == 403\n    resp_text = await resp.text()\n    assert (\n        \"request method 'POST' is not allowed for \"\n        \"'http://client1.example.org' origin\" in resp_text\n    )\n\n    for header_name in {\n        hdrs.ACCESS_CONTROL_ALLOW_ORIGIN,\n        hdrs.ACCESS_CONTROL_ALLOW_CREDENTIALS,\n        hdrs.ACCESS_CONTROL_MAX_AGE,\n        hdrs.ACCESS_CONTROL_EXPOSE_HEADERS,\n        hdrs.ACCESS_CONTROL_ALLOW_METHODS,\n        hdrs.ACCESS_CONTROL_ALLOW_HEADERS,\n    }:\n        assert header_name not in resp.headers\n\n\nasync def test_preflight_req_multiple_routes_with_one_options(aiohttp_client):\n    \"\"\"Test CORS preflight handling on resource that is available through\n    several routes.\n    \"\"\"\n    app = web.Application()\n    cors = _setup(\n        app,\n        defaults={\n            \"*\": ResourceOptions(\n                allow_credentials=True,\n                expose_headers=\"*\",\n                allow_headers=\"*\",\n            )\n        },\n    )\n\n    cors.add(app.router.add_route(\"GET\", \"/{name}\", handler))\n    cors.add(app.router.add_route(\"PUT\", \"/{name}\", handler))\n\n    client = await aiohttp_client(app)\n\n    resp = await client.options(\n        \"/user\",\n        headers={\n            hdrs.ORIGIN: \"http://example.org\",\n            hdrs.ACCESS_CONTROL_REQUEST_METHOD: \"PUT\",\n        },\n    )\n    assert resp.status == 200\n\n    data = await resp.text()\n    assert data == \"\"\n\n\nasync def test_preflight_request_mult_routes_with_one_options_resource(aiohttp_client):\n    \"\"\"Test CORS preflight handling on resource that is available through\n    several routes.\n    \"\"\"\n    app = web.Application()\n    cors = _setup(\n        app,\n        defaults={\n            \"*\": ResourceOptions(\n                allow_credentials=True,\n                expose_headers=\"*\",\n                allow_headers=\"*\",\n            )\n        },\n    )\n\n    resource = cors.add(app.router.add_resource(\"/{name}\"))\n    cors.add(resource.add_route(\"GET\", handler))\n    cors.add(resource.add_route(\"PUT\", handler))\n\n    client = await aiohttp_client(app)\n\n    resp = await client.options(\n        \"/user\",\n        headers={\n            hdrs.ORIGIN: \"http://example.org\",\n            hdrs.ACCESS_CONTROL_REQUEST_METHOD: \"PUT\",\n        },\n    )\n    assert resp.status == 200\n\n    data = await resp.text()\n    assert data == \"\"\n\n\nasync def test_preflight_request_max_age_resource(aiohttp_client):\n    \"\"\"Test CORS preflight handling on resource that is available through\n    several routes.\n    \"\"\"\n    app = web.Application()\n    cors = _setup(\n        app,\n        defaults={\n            \"*\": ResourceOptions(\n                allow_credentials=True,\n                expose_headers=\"*\",\n                allow_headers=\"*\",\n                max_age=1200,\n            )\n        },\n    )\n\n    resource = cors.add(app.router.add_resource(\"/{name}\"))\n    cors.add(resource.add_route(\"GET\", handler))\n\n    client = await aiohttp_client(app)\n\n    resp = await client.options(\n        \"/user\",\n        headers={\n            hdrs.ORIGIN: \"http://example.org\",\n            hdrs.ACCESS_CONTROL_REQUEST_METHOD: \"GET\",\n        },\n    )\n    assert resp.status == 200\n    assert resp.headers[hdrs.ACCESS_CONTROL_MAX_AGE].upper() == \"1200\"\n\n    data = await resp.text()\n    assert data == \"\"\n\n\nasync def test_preflight_request_max_age_webview(aiohttp_client):\n    \"\"\"Test CORS preflight handling on resource that is available through\n    several routes.\n    \"\"\"\n    app = web.Application()\n    cors = _setup(\n        app,\n        defaults={\n            \"*\": ResourceOptions(\n                allow_credentials=True,\n                expose_headers=\"*\",\n                allow_headers=\"*\",\n                max_age=1200,\n            )\n        },\n    )\n\n    class TestView(web.View, CorsViewMixin):\n        async def get(self):\n            resp = web.Response(text=TEST_BODY)\n\n            resp.headers[SERVER_CUSTOM_HEADER_NAME] = SERVER_CUSTOM_HEADER_VALUE\n\n            return resp\n\n    cors.add(app.router.add_route(\"*\", \"/{name}\", TestView))\n\n    client = await aiohttp_client(app)\n\n    resp = await client.options(\n        \"/user\",\n        headers={\n            hdrs.ORIGIN: \"http://example.org\",\n            hdrs.ACCESS_CONTROL_REQUEST_METHOD: \"GET\",\n        },\n    )\n    assert resp.status == 200\n    assert resp.headers[hdrs.ACCESS_CONTROL_MAX_AGE].upper() == \"1200\"\n\n    data = await resp.text()\n    assert data == \"\"\n\n\nasync def test_preflight_request_mult_routes_with_one_options_webview(aiohttp_client):\n    \"\"\"Test CORS preflight handling on resource that is available through\n    several routes.\n    \"\"\"\n    app = web.Application()\n    cors = _setup(\n        app,\n        defaults={\n            \"*\": ResourceOptions(\n                allow_credentials=True,\n                expose_headers=\"*\",\n                allow_headers=\"*\",\n            )\n        },\n    )\n\n    class TestView(web.View, CorsViewMixin):\n        async def get(self):\n            resp = web.Response(text=TEST_BODY)\n\n            resp.headers[SERVER_CUSTOM_HEADER_NAME] = SERVER_CUSTOM_HEADER_VALUE\n\n            return resp\n\n        put = get\n\n    cors.add(app.router.add_route(\"*\", \"/{name}\", TestView))\n\n    client = await aiohttp_client(app)\n\n    resp = await client.options(\n        \"/user\",\n        headers={\n            hdrs.ORIGIN: \"http://example.org\",\n            hdrs.ACCESS_CONTROL_REQUEST_METHOD: \"PUT\",\n        },\n    )\n    assert resp.status == 200\n\n    data = await resp.text()\n    assert data == \"\"\n\n\nasync def test_preflight_request_headers_webview(aiohttp_client):\n    \"\"\"Test CORS preflight request handlers handling.\"\"\"\n    app = web.Application()\n    cors = _setup(\n        app,\n        defaults={\n            \"*\": ResourceOptions(\n                allow_credentials=True,\n                expose_headers=\"*\",\n                allow_headers=(\"Content-Type\", \"X-Header\"),\n            )\n        },\n    )\n\n    class TestView(web.View, CorsViewMixin):\n        async def put(self):\n            response = web.Response(text=TEST_BODY)\n\n            response.headers[SERVER_CUSTOM_HEADER_NAME] = SERVER_CUSTOM_HEADER_VALUE\n\n            return response\n\n    cors.add(app.router.add_route(\"*\", \"/\", TestView))\n\n    client = await aiohttp_client(app)\n\n    resp = await client.options(\n        \"/\",\n        headers={\n            hdrs.ORIGIN: \"http://example.org\",\n            hdrs.ACCESS_CONTROL_REQUEST_METHOD: \"PUT\",\n            hdrs.ACCESS_CONTROL_REQUEST_HEADERS: \"content-type\",\n        },\n    )\n    assert (await resp.text()) == \"\"\n    assert resp.status == 200\n    # Access-Control-Allow-Headers must be compared in case-insensitive\n    # way.\n    assert (\n        resp.headers[hdrs.ACCESS_CONTROL_ALLOW_HEADERS].upper()\n        == \"content-type\".upper()\n    )\n\n    resp = await client.options(\n        \"/\",\n        headers={\n            hdrs.ORIGIN: \"http://example.org\",\n            hdrs.ACCESS_CONTROL_REQUEST_METHOD: \"PUT\",\n            hdrs.ACCESS_CONTROL_REQUEST_HEADERS: \"X-Header,content-type\",\n        },\n    )\n    assert resp.status == 200\n    # Access-Control-Allow-Headers must be compared in case-insensitive\n    # way.\n    assert frozenset(\n        resp.headers[hdrs.ACCESS_CONTROL_ALLOW_HEADERS].upper().split(\",\")\n    ) == {\"X-Header\".upper(), \"content-type\".upper()}\n    assert (await resp.text()) == \"\"\n\n    resp = await client.options(\n        \"/\",\n        headers={\n            hdrs.ORIGIN: \"http://example.org\",\n            hdrs.ACCESS_CONTROL_REQUEST_METHOD: \"PUT\",\n            hdrs.ACCESS_CONTROL_REQUEST_HEADERS: \"content-type,Test\",\n        },\n    )\n    assert resp.status == 403\n    assert hdrs.ACCESS_CONTROL_ALLOW_HEADERS not in resp.headers\n    assert \"headers are not allowed: TEST\" in (await resp.text())\n\n\nasync def test_preflight_request_headers_resource(aiohttp_client):\n    \"\"\"Test CORS preflight request handlers handling.\"\"\"\n    app = web.Application()\n    cors = _setup(\n        app,\n        defaults={\n            \"*\": ResourceOptions(\n                allow_credentials=True,\n                expose_headers=\"*\",\n                allow_headers=(\"Content-Type\", \"X-Header\"),\n            )\n        },\n    )\n\n    cors.add(app.router.add_route(\"PUT\", \"/\", handler))\n\n    client = await aiohttp_client(app)\n\n    resp = await client.options(\n        \"/\",\n        headers={\n            hdrs.ORIGIN: \"http://example.org\",\n            hdrs.ACCESS_CONTROL_REQUEST_METHOD: \"PUT\",\n            hdrs.ACCESS_CONTROL_REQUEST_HEADERS: \"content-type\",\n        },\n    )\n    assert (await resp.text()) == \"\"\n    assert resp.status == 200\n    # Access-Control-Allow-Headers must be compared in case-insensitive\n    # way.\n    assert (\n        resp.headers[hdrs.ACCESS_CONTROL_ALLOW_HEADERS].upper()\n        == \"content-type\".upper()\n    )\n\n    resp = await client.options(\n        \"/\",\n        headers={\n            hdrs.ORIGIN: \"http://example.org\",\n            hdrs.ACCESS_CONTROL_REQUEST_METHOD: \"PUT\",\n            hdrs.ACCESS_CONTROL_REQUEST_HEADERS: \"X-Header,content-type\",\n        },\n    )\n    assert resp.status == 200\n    # Access-Control-Allow-Headers must be compared in case-insensitive\n    # way.\n    assert frozenset(\n        resp.headers[hdrs.ACCESS_CONTROL_ALLOW_HEADERS].upper().split(\",\")\n    ) == {\"X-Header\".upper(), \"content-type\".upper()}\n    assert (await resp.text()) == \"\"\n\n    resp = await client.options(\n        \"/\",\n        headers={\n            hdrs.ORIGIN: \"http://example.org\",\n            hdrs.ACCESS_CONTROL_REQUEST_METHOD: \"PUT\",\n            hdrs.ACCESS_CONTROL_REQUEST_HEADERS: \"content-type,Test\",\n        },\n    )\n    assert resp.status == 403\n    assert hdrs.ACCESS_CONTROL_ALLOW_HEADERS not in resp.headers\n    assert \"headers are not allowed: TEST\" in (await resp.text())\n\n\nasync def test_preflight_request_headers(aiohttp_client):\n    \"\"\"Test CORS preflight request handlers handling.\"\"\"\n    app = web.Application()\n    cors = _setup(\n        app,\n        defaults={\n            \"*\": ResourceOptions(\n                allow_credentials=True,\n                expose_headers=\"*\",\n                allow_headers=(\"Content-Type\", \"X-Header\"),\n            )\n        },\n    )\n\n    resource = cors.add(app.router.add_resource(\"/\"))\n    cors.add(resource.add_route(\"PUT\", handler))\n\n    client = await aiohttp_client(app)\n\n    resp = await client.options(\n        \"/\",\n        headers={\n            hdrs.ORIGIN: \"http://example.org\",\n            hdrs.ACCESS_CONTROL_REQUEST_METHOD: \"PUT\",\n            hdrs.ACCESS_CONTROL_REQUEST_HEADERS: \"content-type\",\n        },\n    )\n    assert (await resp.text()) == \"\"\n    assert resp.status == 200\n    # Access-Control-Allow-Headers must be compared in case-insensitive\n    # way.\n    assert (\n        resp.headers[hdrs.ACCESS_CONTROL_ALLOW_HEADERS].upper()\n        == \"content-type\".upper()\n    )\n\n    resp = await client.options(\n        \"/\",\n        headers={\n            hdrs.ORIGIN: \"http://example.org\",\n            hdrs.ACCESS_CONTROL_REQUEST_METHOD: \"PUT\",\n            hdrs.ACCESS_CONTROL_REQUEST_HEADERS: \"X-Header,content-type\",\n        },\n    )\n    assert resp.status == 200\n    # Access-Control-Allow-Headers must be compared in case-insensitive\n    # way.\n    assert frozenset(\n        resp.headers[hdrs.ACCESS_CONTROL_ALLOW_HEADERS].upper().split(\",\")\n    ) == {\"X-Header\".upper(), \"content-type\".upper()}\n    assert (await resp.text()) == \"\"\n\n    resp = await client.options(\n        \"/\",\n        headers={\n            hdrs.ORIGIN: \"http://example.org\",\n            hdrs.ACCESS_CONTROL_REQUEST_METHOD: \"PUT\",\n            hdrs.ACCESS_CONTROL_REQUEST_HEADERS: \"content-type,Test\",\n        },\n    )\n    assert resp.status == 403\n    assert hdrs.ACCESS_CONTROL_ALLOW_HEADERS not in resp.headers\n    assert \"headers are not allowed: TEST\" in (await resp.text())\n\n\nasync def test_static_route(aiohttp_client):\n    \"\"\"Test a static route with CORS.\"\"\"\n    app = web.Application()\n    cors = _setup(\n        app,\n        defaults={\n            \"*\": ResourceOptions(\n                allow_credentials=True,\n                expose_headers=\"*\",\n                allow_methods=\"*\",\n                allow_headers=(\"Content-Type\", \"X-Header\"),\n            )\n        },\n    )\n\n    test_static_path = pathlib.Path(__file__).parent\n    cors.add(app.router.add_static(\"/static\", test_static_path, name=\"static\"))\n\n    client = await aiohttp_client(app)\n\n    resp = await client.options(\n        \"/static/test_page.html\",\n        headers={\n            hdrs.ORIGIN: \"http://example.org\",\n            hdrs.ACCESS_CONTROL_REQUEST_METHOD: \"OPTIONS\",\n            hdrs.ACCESS_CONTROL_REQUEST_HEADERS: \"content-type\",\n        },\n    )\n    data = await resp.text()\n    assert resp.status == 200\n    assert data == \"\"\n\n\n# TODO: test requesting resources with not configured CORS.\n# TODO: test wildcard origin in default config.\n# TODO: test different combinations of ResourceOptions options.\n# TODO: remove deplication of resource/not resource configuration using\n# pytest's fixtures.\n"
  },
  {
    "path": "tests/integration/test_page.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n<head>\n  <meta charset=\"utf-8\">\n  <title>aiohttp_cors testing</title>\n  <style>\n    body {\n      width: 900px;\n      margin: 0 auto;\n    }\n\n    textarea.results {\n      width: 100%;\n      height: 400px;\n    }\n\n    .serverAddressLabel {\n      display: block;\n      float: left;\n      width: 400px;\n    }\n\n    .templates {\n      display: none;\n    }\n  </style>\n</head>\n<body>\n  <div class=\"templates\">\n    <div id=\"serverAddressTemplate\">\n      <label>\n        <span id=\"label\" class=\"serverAddressLabel\"></span>\n        <input id=\"input\" type=\"text\">\n      </label>\n    </div>\n  </div>\n\n  <form name=\"test\" id=\"testForm\">\n    <div>\n      <input id=\"fillOutButton\" value=\"Fill out addresses\" type=\"button\">\n      <input id=\"runTestsButton\" value=\"Run tests\" type=\"submit\" disabled>\n      <input id=\"clearResultsButton\" value=\"Clear results\" type=\"button\" disabled>\n    </div>\n  </form>\n  <div id=\"log\" class=\"log\">\n  </div>\n  <textarea id=\"results\" class=\"results\">\n  </textarea>\n\n<script>\n(window.cors = (function() {\n  'strict';\n\n  // Copyright 2015 Vladimir Rutsky <vladimir@rutsky.org>\n  // Licensed under the Apache License, Version 2.0.\n\n  var serversNames = [\n    'origin', 'allowing', 'denying', 'free_for_all', 'no_cors'\n  ];\n\n  var tests = {\n    'GET no_cors.json': {\n      // Resource without CORS configuration can be obtained only from origin.\n      method: 'GET',\n      path: 'no_cors.json',\n      allowed: {\n        'origin': true,\n        'allowing': false,\n        'denying': false,\n        'free_for_all': false,\n        'no_cors': false\n      }\n    },\n    'GET cors_resource default': {\n      // Resource with CORS configuration can be obtained only according to\n      // CORS configuration.\n      method: 'GET',\n      path: 'cors_resource',\n      allowed: {\n        'origin': true,\n        'allowing': true,\n        'denying': false,\n        'free_for_all': true,\n        'no_cors': false\n      }\n    },\n    'GET cors_resource with credentials': {\n      // Should be same as without credentials on this route.\n      method: 'GET',\n      path: 'cors_resource',\n      withCredentials: true,\n      allowed: {\n        'origin': true,\n        'allowing': true,\n        'denying': false,\n        'free_for_all': true,\n        'no_cors': false\n      }\n    }\n  };\n\n  function logMessage(level, args) {\n    console[level].apply(console, args);\n\n    var logContainer = document.getElementById('log');\n\n    var logEntry = document.createElement('div');\n    logEntry.classList.add(level);\n\n    var i;\n    for (i = 0; i !== args.length; ++i) {\n      var partData = args[i];\n      var part = document.createElement('span');\n      var txt = document.createTextNode(partData + '');\n      part.appendChild(txt);\n      logEntry.appendChild(part);\n    }\n\n    logContainer.appendChild(logEntry);\n  }\n\n  function log() {\n    logMessage('log', arguments);\n  }\n\n  function error() {\n    logMessage('error', arguments);\n  }\n\n  // Based on an example from <http://www.html5rocks.com/en/tutorials/cors/>.\n  function createCORSRequest(method, url) {\n    var xhr = new XMLHttpRequest();\n    if ('withCredentials' in xhr) {\n      // Check if the XMLHttpRequest object has a 'withCredentials' property.\n      // 'withCredentials' only exists on XMLHTTPRequest2 objects.\n      xhr.open(method, url, true);\n\n    } else if (typeof XDomainRequest !== 'undefined') {\n      // Otherwise, check if XDomainRequest.\n      // XDomainRequest only exists in IE, and is IE's way of making CORS\n      // requests.\n      xhr = new XDomainRequest();\n      xhr.open(method, url);\n\n    } else {\n      // Otherwise, CORS is not supported by the browser.\n      xhr = null;\n    }\n    return xhr;\n  }\n\n  function runTest(testName, resourceUrl, testData, expected, onTestDone) {\n    var xhr = createCORSRequest(testData.method, resourceUrl);\n\n    function done(isResourceObtained) {\n      var result = {};\n\n      var testSucceed = (isResourceObtained === expected);\n      result.status = (testSucceed ? 'success' : 'fail');\n\n      result.data = {\n        responseType: xhr.responseType,\n        response: xhr.response,\n        status: xhr.status,\n        responseHeaders: xhr.getAllResponseHeaders()\n      };\n\n      if (!testSucceed) {\n        error('Test ' + testName + ' failed', result.data);\n      }\n\n      onTestDone(result);\n    }\n\n    if (testData.withCredentials) {\n      xhr.withCredentials = true;\n    }\n\n    xhr.onload = function() {\n      if (xhr.status === 200) {\n        done(true);\n      } else {\n        done(false);\n      }\n    };\n    xhr.onerror = function() {\n      done(false);\n    };\n\n    xhr.send();\n  }\n\n  function onTestDone(state, testName, allDoneCallback, result) {\n    state.allResults[testName] = result;\n    --state.remainingTests;\n\n    if (state.remainingTests === 0) {\n      allDoneCallback(state.allResults);\n    }\n  }\n\n  function countTestsNumber(tests) {\n    var numTests = 0;\n\n    var testName;\n    for (testName in tests) {\n      var test = tests[testName];\n\n      var serverName;\n      for (serverName in test.allowed) {\n        ++numTests;\n      }\n    }\n\n    return numTests;\n  }\n\n  function runAllTests(serversURLs, tests, doneCallback) {\n    var state = {\n      allResults: {},\n      remainingTests: 0\n    };\n\n    state.remainingTests = countTestsNumber(tests);\n\n    var testName;\n    for (testName in tests) {\n      var testData = tests[testName];\n\n      var serverName;\n      for (serverName in testData.allowed) {\n        var expected = testData.allowed[serverName];\n        var serverUrl = serversURLs[serverName];\n        console.assert(serverUrl);\n        var resourceUrl = serverUrl + testData.path;\n\n        var subTestName = testName + '[' + serverName + ']';\n\n        runTest(subTestName, resourceUrl, testData, expected,\n          onTestDone.bind(null, state, subTestName, doneCallback));\n      }\n    }\n  }\n\n  function setResults(object) {\n    var results = document.getElementById('results');\n    results.value = JSON.stringify(object, null, ' ');\n  }\n\n  function setExceptionAsResult(ex) {\n    error(ex);\n\n    setResults({\n      'status': 'error',\n      'error': ex.toString(),\n      'data': {\n        message: ex.message,\n        // Firefox-specific\n        fileName: ex.fileName,\n        lineNumber: ex.lineNumber,\n        columnNumber: ex.columnNumber,\n        stack: ex.stack,\n        // IE-specific\n        description: ex.description\n      }\n    });\n  }\n\n  function serverInputId(serverName) {\n    return 'server_' + serverName;\n  }\n\n  function formatTemplate(node, serverName) {\n    if (node.id === 'label') {\n      node.innerHTML = 'address of \"' + serverName + '\" server:';\n      node.removeAttribute('id');\n    }\n\n    if (node.id === 'input') {\n      node.id = serverInputId(serverName);\n    }\n\n    var i;\n    for (i = 0; i !== node.children.length; ++i) {\n      formatTemplate(node.children[i], serverName);\n    }\n  }\n\n  function createAddressInputFields() {\n    var template = document.getElementById('serverAddressTemplate');\n    var testForm = document.getElementById('testForm');\n\n    var i;\n    for (i = 0; i !== serversNames.length; ++i) {\n      var serverName = serversNames[i];\n\n      var node = template.cloneNode(true);\n      node.removeAttribute('id');\n\n      formatTemplate(node, serverName);\n\n      testForm.insertBefore(node, testForm.firstChild);\n    }\n  }\n\n  function setServerAddr(serverName, value) {\n    var input = document.getElementById(serverInputId(serverName));\n    input.value = value;\n  }\n\n  function getServerAddrOptional(serverName) {\n    var input = document.getElementById(serverInputId(serverName));\n    var addr = input.value;\n    if (!addr) {\n      return null;\n    }\n\n    if (addr[addr.length - 1] !== '/') {\n      addr += '/';\n    }\n\n    return addr;\n  }\n\n  function getServerAddr(serverName) {\n    var addr = getServerAddrOptional(serverName);\n\n    if (!addr) {\n      throw new Error(\n        'Server address for \"' + serverName + '\" is not specified');\n    }\n\n    return addr;\n  }\n\n  function getServersUrls() {\n    var serverUrls = {};\n\n    var i;\n    for (i = 0; i !== serversNames.length; ++i) {\n      var serverName = serversNames[i];\n      serverUrls[serverName] = getServerAddr(serverName);\n    }\n\n    return serverUrls;\n  }\n\n  function setServersUrls(serverUrls) {\n    var serverName;\n    for (serverName in serverUrls) {\n      setServerAddr(serverName, serverUrls[serverName]);\n    }\n  }\n\n  function runTests() {\n    setTestingStarted();\n\n    var results = document.getElementById('results');\n\n    try {\n      var serversURLs = getServersUrls();\n      results = runAllTests(serversURLs, tests, function(results) {\n        setResults({\n          'status': 'success',\n          'data': results\n        });\n\n        var numFailed = 0;\n        var numTests = 0;\n        var failedTests = {};\n        var testName;\n        for (testName in results) {\n          ++numTests;\n          if (results[testName].status !== 'success') {\n            ++numFailed;\n            failedTests[testName] = results[testName];\n          }\n        }\n\n        if (numFailed > 0) {\n          error(\n            'Failed ' + numFailed + ' tests of ' + numTests, failedTests);\n        } else {\n          log('All ' + numTests + ' tests passed');\n        }\n\n        setTestingFinished();\n      });\n    } catch(ex) {\n      setExceptionAsResult(ex);\n    }\n  }\n\n  function fillOutAddresses(callback) {\n    var originAddr = getServerAddrOptional('origin');\n    if (!originAddr) {\n      originAddr = '/';\n    }\n    var addressesUrl = originAddr + 'servers_addresses';\n    var xhr = createCORSRequest('GET', addressesUrl);\n\n    xhr.onload = function() {\n      if (xhr.status !== 200) {\n        error(\n          '/servers_addresses request failed with status ' + xhr.status,\n          xhr.responseText);\n\n      } else {\n        log('Received server addresses:', xhr.response);\n        setServersUrls(JSON.parse(xhr.responseText));\n      }\n\n      if (typeof callback !== 'undefined') {\n        callback();\n      }\n    };\n\n    xhr.onerror = function() {\n      error('/servers_addresses request failed');\n    };\n\n    xhr.send();\n  }\n\n  function setReadyToStartTesting() {\n    var runButton = document.getElementById('runTestsButton');\n    runButton.disabled = false;\n  }\n\n  function setTestingStarted() {\n    var runButton = document.getElementById('runTestsButton');\n    var clearButton = document.getElementById('clearResultsButton');\n    runButton.disabled = true;\n    clearButton.disabled = true;\n  }\n\n  function setTestingFinished() {\n    var runButton = document.getElementById('runTestsButton');\n    var clearButton = document.getElementById('clearResultsButton');\n    runButton.disabled = false;\n    clearButton.disabled = false;\n  }\n\n  return {\n    runTests: runTests,\n    fillOutAddresses: fillOutAddresses,\n    createAddressInputFields: createAddressInputFields,\n    setReadyToStartTesting: setReadyToStartTesting\n  };\n}()));\n\n(function() {\n  var cors = window.cors;\n\n  cors.createAddressInputFields();\n\n  var testForm = document.getElementById('testForm');\n  testForm.onsubmit = function() {\n    cors.runTests();\n    return false;\n  };\n\n  var fillOutButton = document.getElementById('fillOutButton');\n  fillOutButton.onclick = function() {\n    cors.fillOutAddresses();\n    return false;\n  };\n\n  cors.fillOutAddresses(cors.setReadyToStartTesting);\n}());\n</script>\n</body>\n</html>\n"
  },
  {
    "path": "tests/integration/test_real_browser.py",
    "content": "# Copyright 2015 Vladimir Rutsky <vladimir@rutsky.org>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"System test using real browser.\"\"\"\n\nimport asyncio\nimport json\nimport logging\nimport os\nimport pathlib\nimport socket\nimport webbrowser\n\nimport pytest\nimport selenium.common.exceptions\nfrom selenium import webdriver\nfrom selenium.webdriver.common.by import By\nfrom selenium.webdriver.common.keys import Keys\nfrom selenium.webdriver.support import expected_conditions as EC\nfrom selenium.webdriver.support.ui import WebDriverWait\n\nfrom aiohttp import hdrs, web\nfrom aiohttp_cors import CorsViewMixin, ResourceOptions, setup as _setup\n\n\nclass _ServerDescr:\n    \"\"\"Auxiliary class for storing server info\"\"\"\n\n    def __init__(self):\n        self.app = None\n        self.cors = None\n        self.handler = None\n        self.server = None\n        self.url = None\n\n\nclass IntegrationServers:\n    \"\"\"Integration servers starting/stopping manager\"\"\"\n\n    def __init__(self, use_resources, use_webview, *, loop=None):\n        self.servers = {}\n\n        self.loop = loop\n        if self.loop is None:\n            self.loop = asyncio.get_event_loop()\n\n        self.use_resources = use_resources\n        self.use_webview = use_webview\n\n        self._logger = logging.getLogger(\"IntegrationServers\")\n\n    @property\n    def origin_server_url(self):\n        return self.servers[\"origin\"].url\n\n    async def start_servers(self):\n        test_page_path = pathlib.Path(__file__).with_name(\"test_page.html\")\n\n        async def handle_test_page(request: web.Request) -> web.StreamResponse:\n            with test_page_path.open(\"r\", encoding=\"utf-8\") as f:\n                return web.Response(\n                    text=f.read(), headers={hdrs.CONTENT_TYPE: \"text/html\"}\n                )\n\n        async def handle_no_cors(request: web.Request) -> web.StreamResponse:\n            return web.Response(\n                text=\"\"\"{\"type\": \"no_cors.json\"}\"\"\",\n                headers={hdrs.CONTENT_TYPE: \"application/json\"},\n            )\n\n        async def handle_resource(request: web.Request) -> web.StreamResponse:\n            return web.Response(\n                text=\"\"\"{\"type\": \"resource\"}\"\"\",\n                headers={hdrs.CONTENT_TYPE: \"application/json\"},\n            )\n\n        async def handle_servers_addresses(request: web.Request) -> web.StreamResponse:\n            servers_addresses = {\n                name: descr.url for name, descr in self.servers.items()\n            }\n            return web.Response(text=json.dumps(servers_addresses))\n\n        class ResourceView(web.View, CorsViewMixin):\n\n            async def get(self) -> web.StreamResponse:\n                return await handle_resource(self.request)\n\n        # For most resources:\n        # \"origin\" server has no CORS configuration.\n        # \"allowing\" server explicitly allows CORS requests to \"origin\" server.\n        # \"denying\" server explicitly disallows CORS requests to \"origin\"\n        # server.\n        # \"free_for_all\" server allows CORS requests for all origins server.\n        # \"no_cors\" server has no CORS configuration.\n        cors_server_names = [\"allowing\", \"denying\", \"free_for_all\"]\n        server_names = cors_server_names + [\"origin\", \"no_cors\"]\n\n        for server_name in server_names:\n            assert server_name not in self.servers\n            self.servers[server_name] = _ServerDescr()\n\n        server_sockets = {}\n\n        # Create applications and sockets.\n        for server_name, server_descr in self.servers.items():\n            server_descr.app = web.Application()\n\n            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n            sock.bind((\"127.0.0.1\", 0))\n            sock.listen(10)\n            server_sockets[server_name] = sock\n\n            hostaddr, port = sock.getsockname()\n            server_descr.url = f\"http://{hostaddr}:{port}\"\n\n        # Server test page from origin server.\n        self.servers[\"origin\"].app.router.add_route(\"GET\", \"/\", handle_test_page)\n        self.servers[\"origin\"].app.router.add_route(\n            \"GET\", \"/servers_addresses\", handle_servers_addresses\n        )\n\n        # Add routes to all servers.\n        for server_name in server_names:\n            app = self.servers[server_name].app\n            app.router.add_route(\"GET\", \"/no_cors.json\", handle_no_cors)\n            if self.use_webview:\n                app.router.add_route(\n                    \"*\", \"/cors_resource\", ResourceView, name=\"cors_resource\"\n                )\n            else:\n                app.router.add_route(\n                    \"GET\", \"/cors_resource\", handle_resource, name=\"cors_resource\"\n                )\n\n        cors_default_configs = {\n            \"allowing\": {\n                self.servers[\"origin\"].url: ResourceOptions(\n                    allow_credentials=True, expose_headers=\"*\", allow_headers=\"*\"\n                )\n            },\n            \"denying\": {\n                # Allow requests to other than \"origin\" server.\n                self.servers[\"allowing\"].url: ResourceOptions(\n                    allow_credentials=True, expose_headers=\"*\", allow_headers=\"*\"\n                )\n            },\n            \"free_for_all\": {\n                \"*\": ResourceOptions(\n                    allow_credentials=True, expose_headers=\"*\", allow_headers=\"*\"\n                )\n            },\n        }\n\n        # Configure CORS.\n        for server_name, server_descr in self.servers.items():\n            default_config = cors_default_configs.get(server_name)\n            if default_config is None:\n                continue\n            server_descr.cors = _setup(server_descr.app, defaults=default_config)\n\n        # Add CORS routes.\n        for server_name in cors_server_names:\n            server_descr = self.servers[server_name]\n            # TODO: Starting from aiohttp 0.21.0 name-based access returns\n            # Resource, not Route. Manually get route while aiohttp_cors\n            # doesn't support configuring for Resources.\n            resource = server_descr.app.router[\"cors_resource\"]\n            route = next(iter(resource))\n            if self.use_resources:\n                server_descr.cors.add(resource)\n                server_descr.cors.add(route)\n\n            elif self.use_webview:\n                server_descr.cors.add(route)\n\n            else:\n                server_descr.cors.add(route)\n\n        # Start servers.\n        for server_name, server_descr in self.servers.items():\n            runner = web.AppRunner(server_descr.app)\n            await runner.setup()\n            site = web.SockSite(runner, server_sockets[server_name])\n            await site.start()\n            server_descr.runner = runner\n\n            self._logger.info(\n                \"Started server '%s' at '%s'\", server_name, server_descr.url\n            )\n\n    async def stop_servers(self):\n        for server_descr in self.servers.values():\n            runner = server_descr.runner\n            await runner.shutdown()\n            await runner.cleanup()\n\n        self.servers = {}\n\n\ndef _get_chrome_driver():\n    driver_path_env = \"WEBDRIVER_CHROMEDRIVER_PATH\"\n\n    if driver_path_env in os.environ:\n        driver = webdriver.Chrome(executable_path=os.environ[driver_path_env])\n    else:\n        driver = webdriver.Chrome()\n\n    return driver\n\n\n@pytest.fixture(params=[(False, False), (True, False), (False, True)])\ndef server(request, loop):\n    async def inner():\n        # to grab implicit loop\n        return IntegrationServers(*request.param)\n\n    return loop.run_until_complete(inner())\n\n\n@pytest.fixture(params=[webdriver.Firefox, _get_chrome_driver])\ndef driver(request):\n    try:\n        driver = request.param()\n    except selenium.common.exceptions.WebDriverException:\n        pytest.skip(\"Driver is not supported\")\n\n    yield driver\n    driver.close()\n\n\nasync def test_in_webdriver(driver, server):\n    loop = asyncio.get_event_loop()\n    await server.start_servers()\n\n    def selenium_thread():\n        driver.get(server.origin_server_url)\n        assert \"aiohttp_cors\" in driver.title\n\n        wait = WebDriverWait(driver, 10)\n\n        run_button = wait.until(EC.element_to_be_clickable((By.ID, \"runTestsButton\")))\n\n        # Start tests.\n        run_button.send_keys(Keys.RETURN)\n\n        # Wait while test will finish (until clear button is not\n        # activated).\n        wait.until(EC.element_to_be_clickable((By.ID, \"clearResultsButton\")))\n\n        # Get results json\n        results_area = driver.find_element(By.ID, \"results\")\n\n        return json.loads(results_area.get_attribute(\"value\"))\n\n    try:\n        results = await loop.run_in_executor(None, selenium_thread)\n\n        assert results[\"status\"] == \"success\"\n        for test_name, test_data in results[\"data\"].items():\n            assert test_data[\"status\"] == \"success\"\n\n    finally:\n        await server.stop_servers()\n\n\ndef _run_integration_server():\n    \"\"\"Runs integration server for interactive debugging.\"\"\"\n\n    logging.basicConfig(level=logging.INFO)\n\n    logger = logging.getLogger(\"run_integration_server\")\n\n    loop = asyncio.get_event_loop()\n\n    servers = IntegrationServers(False, True)\n    logger.info(\"Starting integration servers...\")\n    loop.run_until_complete(servers.start_servers())\n\n    try:\n        webbrowser.open(servers.origin_server_url)\n    except webbrowser.Error:\n        pass\n\n    try:\n        loop.run_forever()\n    except KeyboardInterrupt:\n        pass\n    finally:\n        logger.info(\"Stopping integration servers...\")\n        loop.run_until_complete(servers.stop_servers())\n\n\nif __name__ == \"__main__\":\n    # This module can be run in the following way:\n    #     $ python -m tests.integration.test_real_browser\n    # from aiohttp_cors root directory.\n    _run_integration_server()\n"
  },
  {
    "path": "tests/unit/__init__.py",
    "content": ""
  },
  {
    "path": "tests/unit/test___about__.py",
    "content": "# Copyright 2015 Vladimir Rutsky <vladimir@rutsky.org>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Test aiohttp_cors package metainformation.\"\"\"\n\nfrom packaging.version import parse\n\nimport aiohttp_cors\n\n\ndef test_version():\n    \"\"\"Test package version string\"\"\"\n    # not raised\n    parse(aiohttp_cors.__version__)\n"
  },
  {
    "path": "tests/unit/test_cors_config.py",
    "content": "# Copyright 2015 Vladimir Rutsky <vladimir@rutsky.org>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"aiohttp_cors.cors_config unit tests.\"\"\"\n\nimport pytest\n\nfrom aiohttp import web\nfrom aiohttp_cors import CorsConfig, CorsViewMixin, ResourceOptions\n\n\nasync def _handler(request):\n    return web.Response(text=\"Done\")\n\n\nclass _View(web.View, CorsViewMixin):\n\n    async def get(self):\n        return web.Response(text=\"Done\")\n\n\n@pytest.fixture\ndef app():\n    return web.Application()\n\n\n@pytest.fixture\ndef cors(app):\n    return CorsConfig(app, defaults={\"*\": ResourceOptions()})\n\n\n@pytest.fixture\ndef get_route(app):\n    return app.router.add_route(\"GET\", \"/get_path\", _handler)\n\n\n@pytest.fixture\ndef options_route(app):\n    return app.router.add_route(\"OPTIONS\", \"/options_path\", _handler)\n\n\ndef test_add_options_route(app, cors, options_route):\n    \"\"\"Test configuring OPTIONS route\"\"\"\n    with pytest.raises(ValueError, match=\"already has OPTIONS handler\"):\n        cors.add(options_route.resource)\n\n\ndef test_plain_named_route(app, cors):\n    \"\"\"Test adding plain named route.\"\"\"\n    # Adding CORS routes should not introduce new named routes.\n    assert len(app.router.keys()) == 0\n    route = app.router.add_route(\"GET\", \"/{name}\", _handler, name=\"dynamic_named_route\")\n    assert len(app.router.keys()) == 1\n    cors.add(route)\n    assert len(app.router.keys()) == 1\n\n\ndef test_dynamic_named_route(app, cors):\n    \"\"\"Test adding dynamic named route.\"\"\"\n    assert len(app.router.keys()) == 0\n    route = app.router.add_route(\"GET\", \"/{name}\", _handler, name=\"dynamic_named_route\")\n    assert len(app.router.keys()) == 1\n    cors.add(route)\n    assert len(app.router.keys()) == 1\n\n\ndef test_static_named_route(app, cors):\n    \"\"\"Test adding dynamic named route.\"\"\"\n    assert len(app.router.keys()) == 0\n    route = app.router.add_static(\"/file\", \"/\", name=\"dynamic_named_route\")\n    assert len(app.router.keys()) == 1\n    cors.add(route)\n    assert len(app.router.keys()) == 1\n\n\ndef test_static_resource(app, cors):\n    \"\"\"Test adding static resource.\"\"\"\n    assert len(app.router.keys()) == 0\n    app.router.add_static(\"/file\", \"/\", name=\"dynamic_named_route\")\n    assert len(app.router.keys()) == 1\n    for resource in list(app.router.resources()):\n        if isinstance(resource, web.StaticResource):\n            cors.add(resource)\n    assert len(app.router.keys()) == 1\n\n\ndef test_web_view_resource(app, cors):\n    \"\"\"Test adding resource with web.View as handler\"\"\"\n    assert len(app.router.keys()) == 0\n    route = app.router.add_route(\"GET\", \"/{name}\", _View, name=\"dynamic_named_route\")\n    assert len(app.router.keys()) == 1\n    cors.add(route)\n    assert len(app.router.keys()) == 1\n\n\ndef test_web_view_warning(app, cors):\n    \"\"\"Test adding resource with web.View as handler\"\"\"\n    route = app.router.add_route(\"*\", \"/\", _View)\n    with pytest.warns(DeprecationWarning):\n        cors.add(route, webview=True)\n\n\ndef test_disable_bare_view(app, cors):\n    class View(web.View):\n        pass\n\n    route = app.router.add_route(\"*\", \"/\", View)\n    with pytest.raises(ValueError):\n        cors.add(route)\n"
  },
  {
    "path": "tests/unit/test_mixin.py",
    "content": "import asyncio\nfrom unittest import mock\n\nimport pytest\n\nfrom aiohttp import web\nfrom aiohttp_cors import (\n    APP_CONFIG_KEY,\n    CorsConfig,\n    CorsViewMixin,\n    ResourceOptions,\n    custom_cors,\n)\n\nDEFAULT_CONFIG = {\"*\": ResourceOptions()}\n\nCLASS_CONFIG = {\"*\": ResourceOptions()}\n\nCUSTOM_CONFIG = {\"www.client1.com\": ResourceOptions(allow_headers=[\"X-Host\"])}\n\n\nclass SimpleView(web.View, CorsViewMixin):\n    async def get(self):\n        return web.Response(text=\"Done\")\n\n\nclass SimpleViewWithConfig(web.View, CorsViewMixin):\n\n    cors_config = CLASS_CONFIG\n\n    async def get(self):\n        return web.Response(text=\"Done\")\n\n\nclass CustomMethodView(web.View, CorsViewMixin):\n\n    cors_config = CLASS_CONFIG\n\n    async def get(self):\n        return web.Response(text=\"Done\")\n\n    @custom_cors(CUSTOM_CONFIG)\n    async def post(self):\n        return web.Response(text=\"Done\")\n\n\n@pytest.fixture\ndef _app():\n    return web.Application()\n\n\n@pytest.fixture\ndef cors(_app):\n    ret = CorsConfig(_app, defaults=DEFAULT_CONFIG)\n    _app[APP_CONFIG_KEY] = ret\n    return ret\n\n\n@pytest.fixture\ndef app(_app, cors):\n    # a trick to install a cors into app\n    return _app\n\n\ndef test_raise_exception_when_cors_not_configure():\n    request = mock.Mock()\n    request.app = {}\n    view = CustomMethodView(request)\n\n    with pytest.raises(ValueError):\n        view.get_request_config(request, \"post\")\n\n\nasync def test_raises_forbidden_when_config_not_found(app):\n    app[APP_CONFIG_KEY].defaults = {}\n    request = mock.Mock()\n    request.app = app\n    request.headers = {\"Origin\": \"*\", \"Access-Control-Request-Method\": \"GET\"}\n    view = SimpleView(request)\n\n    with pytest.raises(web.HTTPForbidden):\n        await view.options()\n\n\ndef test_method_with_custom_cors(app):\n    \"\"\"Test adding resource with web.View as handler\"\"\"\n    request = mock.Mock()\n    request.app = app\n    view = CustomMethodView(request)\n\n    assert hasattr(view.post, \"post_cors_config\")\n    assert asyncio.iscoroutinefunction(view.post)\n    config = view.get_request_config(request, \"post\")\n\n    assert config.get(\"www.client1.com\") == CUSTOM_CONFIG[\"www.client1.com\"]\n\n\ndef test_method_with_class_config(app):\n    \"\"\"Test adding resource with web.View as handler\"\"\"\n    request = mock.Mock()\n    request.app = app\n    view = SimpleViewWithConfig(request)\n\n    assert not hasattr(view.get, \"get_cors_config\")\n    config = view.get_request_config(request, \"get\")\n\n    assert config.get(\"*\") == CLASS_CONFIG[\"*\"]\n\n\ndef test_method_with_default_config(app):\n    \"\"\"Test adding resource with web.View as handler\"\"\"\n    request = mock.Mock()\n    request.app = app\n    view = SimpleView(request)\n\n    assert not hasattr(view.get, \"get_cors_config\")\n    config = view.get_request_config(request, \"get\")\n\n    assert config.get(\"*\") == DEFAULT_CONFIG[\"*\"]\n"
  },
  {
    "path": "tests/unit/test_preflight_handler.py",
    "content": "from unittest import mock\n\nimport pytest\n\nfrom aiohttp_cors.preflight_handler import _PreflightHandler\n\n\nasync def test_raises_when_handler_not_extend():\n    request = mock.Mock()\n    handler = _PreflightHandler()\n    with pytest.raises(NotImplementedError):\n        await handler._get_config(request, \"origin\", \"GET\")\n"
  },
  {
    "path": "tests/unit/test_resource_options.py",
    "content": "# Copyright 2015 Vladimir Rutsky <vladimir@rutsky.org>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"aiohttp_cors.resource_options unit tests.\"\"\"\n\nimport pytest\n\nfrom aiohttp_cors.resource_options import ResourceOptions\n\n\ndef test_init_no_args():\n    \"\"\"Test construction without arguments\"\"\"\n    opts = ResourceOptions()\n\n    assert not opts.allow_credentials\n    assert not opts.expose_headers\n    assert not opts.allow_headers\n    assert opts.max_age is None\n\n\ndef test_comparison():\n    assert ResourceOptions() == ResourceOptions()\n    assert not (ResourceOptions() != ResourceOptions())\n    assert not (ResourceOptions(allow_credentials=True) == ResourceOptions())\n    assert ResourceOptions(allow_credentials=True) != ResourceOptions()\n\n\ndef test_allow_methods():\n    assert ResourceOptions().allow_methods is None\n    assert ResourceOptions(allow_methods=\"*\").allow_methods == \"*\"\n    assert ResourceOptions(allow_methods=[]).allow_methods == frozenset()\n    assert ResourceOptions(allow_methods=[\"get\"]).allow_methods == frozenset([\"GET\"])\n    assert ResourceOptions(allow_methods=[\"get\", \"Post\"]).allow_methods == {\n        \"GET\",\n        \"POST\",\n    }\n    with pytest.raises(ValueError):\n        ResourceOptions(allow_methods=\"GET\")\n\n\n# TODO: test arguments parsing\n"
  },
  {
    "path": "tests/unit/test_urldispatcher_router_adapter.py",
    "content": "# Copyright 2015 Vladimir Rutsky <vladimir@rutsky.org>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"aiohttp_cors.urldispatcher_router_adapter unit tests.\"\"\"\n\nfrom unittest import mock\n\nimport pytest\n\nfrom aiohttp import web\nfrom aiohttp_cors import ResourceOptions\nfrom aiohttp_cors.urldispatcher_router_adapter import (\n    ResourcesUrlDispatcherRouterAdapter,\n)\n\n\nasync def _handler(request):\n    return web.Response(text=\"Done\")\n\n\n@pytest.fixture\ndef app():\n    return web.Application()\n\n\n@pytest.fixture\ndef adapter(app):\n    return ResourcesUrlDispatcherRouterAdapter(\n        app.router, defaults={\"*\": ResourceOptions()}\n    )\n\n\n@pytest.fixture\ndef get_route(app):\n    return app.router.add_route(\"GET\", \"/get_path\", _handler)\n\n\n@pytest.fixture\ndef options_route(app):\n    return app.router.add_route(\"OPTIONS\", \"/options_path\", _handler)\n\n\ndef test_add_get_route(adapter, get_route):\n    \"\"\"Test configuring GET route\"\"\"\n    result = adapter.add_preflight_handler(get_route.resource, _handler)\n    assert result is None\n\n    assert len(adapter._resource_config) == 0\n    assert len(adapter._resources_with_preflight_handlers) == 1\n    assert len(adapter._preflight_routes) == 1\n\n\ndef test_add_options_route(adapter, options_route):\n    \"\"\"Test configuring OPTIONS route\"\"\"\n\n    adapter.add_preflight_handler(options_route, _handler)\n\n    assert not adapter._resources_with_preflight_handlers\n    assert not adapter._preflight_routes\n\n\ndef test_get_non_preflight_request_config(adapter, get_route):\n    adapter.add_preflight_handler(get_route.resource, _handler)\n    adapter.set_config_for_routing_entity(\n        get_route.resource,\n        {\n            \"http://example.org\": ResourceOptions(),\n        },\n    )\n\n    adapter.add_preflight_handler(get_route, _handler)\n    adapter.set_config_for_routing_entity(\n        get_route,\n        {\n            \"http://test.example.org\": ResourceOptions(),\n        },\n    )\n\n    request = mock.Mock()\n\n    with mock.patch(\n        \"aiohttp_cors.urldispatcher_router_adapter.\"\n        \"ResourcesUrlDispatcherRouterAdapter.\"\n        \"is_cors_enabled_on_request\"\n    ) as is_cors_enabled_on_request, mock.patch(\n        \"aiohttp_cors.urldispatcher_router_adapter.\"\n        \"ResourcesUrlDispatcherRouterAdapter.\"\n        \"_request_resource\"\n    ) as _request_resource:\n        is_cors_enabled_on_request.return_value = True\n        _request_resource.return_value = get_route.resource\n\n        assert adapter.get_non_preflight_request_config(request) == {\n            \"*\": ResourceOptions(),\n            \"http://example.org\": ResourceOptions(),\n        }\n\n        request.method = \"GET\"\n\n        assert adapter.get_non_preflight_request_config(request) == {\n            \"*\": ResourceOptions(),\n            \"http://example.org\": ResourceOptions(),\n            \"http://test.example.org\": ResourceOptions(),\n        }\n"
  }
]