[
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Release\n\non:\n  push:\n  pull_request:\n\njobs:\n\n  build:\n    name: Build\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v4\n    - uses: actions/setup-python@v5\n    - name: Install build\n      run: pip install build\n    - name: Build distribution\n      run: python -m build .\n    - name: File list\n      run: tar tf dist/*.tar.gz && unzip -l dist/*.whl\n    - name: Upload artifacts\n      uses: actions/upload-artifact@v4\n      with:\n        name: dist\n        path: dist/\n\n  test:\n    name: Run tests\n    needs: build\n    strategy:\n      matrix:\n        python-version: [\"3.8\", \"3.9\", \"3.10\", \"3.11\", \"3.12\", \"3.13.0-rc.1\"]\n        os: [ubuntu-latest, macos-13, macos-latest, windows-latest]\n    runs-on: ${{ matrix.os }}\n    steps:\n    - uses: actions/checkout@v4\n    - uses: actions/setup-python@v5\n      with:\n        python-version: ${{ matrix.python-version }}\n    - name: Download artifacts\n      uses: actions/download-artifact@v4\n    - name: Install wheel\n      shell: bash\n      run: pip install dist/*.whl\n    - name: Make it use package from wheel\n      shell: bash\n      run: rm flask_shell_ipython.py\n    - name: Install test requirements\n      run: pip install -r requirements-test.txt\n    - name: Test\n      if: runner.os != 'Windows'\n      run: pytest --forked\n    - name: Test (Windows)\n      if: runner.os == 'Windows'\n      shell: bash\n      run: pytest --collect-only -q | grep ^test_ | while read testname; do pytest -q $testname; done\n\n  pypi-publish-test:\n    name: Release on Test PyPI\n    needs: test\n    runs-on: ubuntu-latest\n    environment:\n      name: Test PyPI\n      url: \"https://test.pypi.org/project/flask-shell-ipython/\"\n    permissions:\n      id-token: write\n    steps:\n      - name: Download artifacts\n        uses: actions/download-artifact@v4\n      - name: Publish package distributions to PyPI\n        uses: pypa/gh-action-pypi-publish@release/v1\n        with:\n          repository-url: \"https://test.pypi.org/legacy/\"\n\n  github-release:\n    name: Release on GitHub\n    needs: test\n    if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')\n    runs-on: ubuntu-latest\n    environment:\n      name: GitHub\n      url: \"https://github.com/ei-grad/flask-shell-ipython/releases/\"\n    permissions:\n      attestations: write\n      contents: write\n      id-token: write\n    steps:\n      - uses: actions/checkout@v4\n      - name: Extract Version from Tag\n        run: echo \"VERSION_FROM_GIT_REF=${GITHUB_REF#refs/tags/v}\" >> $GITHUB_ENV\n      - name: Extract Version from pyproject.toml\n        run: |\n          pip install toml\n          VERSION_FROM_PYPROJECT=$(python << EOF\n          import toml\n          print(toml.load('pyproject.toml')['project']['version'])\n          EOF\n          )\n          echo \"VERSION_FROM_PYPROJECT=$VERSION_FROM_PYPROJECT\" >> $GITHUB_ENV\n      - name: Ensure version consistency\n        run: |\n          if [ \"$VERSION_FROM_GIT_REF\" != \"$VERSION_FROM_PYPROJECT\" ]; then\n            echo \"Error: Version from tag ($VERSION_FROM_GIT_REF) does not match version in pyproject.toml ($VERSION_FROM_PYPROJECT)\"\n            exit 1\n          fi\n          echo VERSION=$VERSION_FROM_GIT_REF >> $GITHUB_ENV\n      - name: Extract changelog for release notes\n        run: |\n          (\n            echo \"CHANGELOG<<EOF\"\n            awk -v version=\"$VERSION\" '{\n                if ($0 ~ \"^## \\\\[\" version \"\\\\]\") inSection = 1;\n                else if ($0 ~ \"^## \\\\[\" && inSection) inSection = 0;\n                if (inSection) print $0;\n            }' CHANGELOG.md\n            echo \"EOF\"\n          ) >> $GITHUB_ENV\n      - name: Validate changelog content\n        run: |\n          if [ -z \"$CHANGELOG\" ] ; then\n            echo \"Missing CHANGELOG.md section for release $VERSION\"\n            exit 1\n          fi\n      - name: Download artifacts\n        uses: actions/download-artifact@v4\n      - name: Attest build provenance\n        uses: actions/attest-build-provenance@v1\n        with:\n          subject-path: dist/*\n      - name: Create GitHub Release\n        env:\n          GITHUB_TOKEN: ${{ github.token }}\n        run: >-\n          gh release create \"$VERSION\"\n          --draft\n          --notes \"$CHANGELOG\"\n      - name: Upload artifact signatures to GitHub Release\n        env:\n          GITHUB_TOKEN: ${{ github.token }}\n        run: >-\n          gh release upload \"$VERSION\" dist/**\n\n  pypi-publish:\n    name: Release on PyPI\n    needs: github-release\n    if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')\n    runs-on: ubuntu-latest\n    environment:\n      name: PyPI\n      url: \"https://pypi.org/project/flask-shell-ipython/\"\n    permissions:\n      id-token: write\n    steps:\n      - name: Download artifacts\n        uses: actions/download-artifact@v4\n      - name: Publish package distributions to PyPI\n        uses: pypa/gh-action-pypi-publish@release/v1\n"
  },
  {
    "path": ".gitignore",
    "content": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nenv/\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\n*.egg-info/\n.installed.cfg\n*.egg\n\n# PyInstaller\n#  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*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*,cover\n.hypothesis/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\nlocal_settings.py\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\ntarget/\n\n# IPython Notebook\n.ipynb_checkpoints\n\n# pyenv\n.python-version\n\n# celery beat schedule file\ncelerybeat-schedule\n\n# dotenv\n.env\n\n# virtualenv\nvenv/\nENV/\n\n# Spyder project settings\n.spyderproject\n\n# Rope project settings\n.ropeproject\n\nflask_shell_ipython-*/\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\n## [0.5.4] - 2024-09-10\n### Packaging\n- Check consistency between tag version and `pyproject.toml` version.\n- Added extraction of changelog content for release notes from `CHANGELOG.md`.\n- Switched to `actions/attest-build-provenance@v1` from `gh-action-sigstore-python`.\n\n## [0.5.3] - 2024-09-06\n### Packaging\n- Updated `gh-action-sigstore-python` version to `v3.0.0` in GitHub Actions workflow.\n\n## [0.5.2] - 2024-09-05\n### Packaging\n- Migrated to `pyproject.toml` and Hatchling build system.\n- Added Python 3.12 and 3.13.0-rc.1 to the test matrix.\n- Updated CI workflows and actions to the latest versions.\n- Enabled attestations for publishing.\n- Added publishing to Test PyPI and GitHub releases.\n- Dropped support for Python 3.6 and 3.7 due to end-of-life.\n\n## [0.5.1] - 2023-04-27\n### Added\n- Reverted to original pre-2018 banner format in the IPython shell.\n- Minor fixes for banner formatting.\n\n## [0.5.0] - 2023-04-27\n### Added\n- Implemented a new release format and workflow.\n- Drop Python 2.x support.\n- Updated dependencies in `setup.py`.\n\n## [0.4.1] - 2022-05-06\n### Added\n- Universal wheel support added in `setup.cfg`.\n- Packaging improvements.\n\n## [0.4.0] - 2019-12-06\n### Fixed\n- Improved consistency with built-in Flask shell banner format.\n- Replaced deprecated `_app_ctx_stack` with `current_app`.\n\n## [0.3.1] - 2018-08-31\n### Fixed\n- Grammar corrections in docstrings.\n- Fixed minor issues with Python 3 compatibility.\n\n## [0.3.0] - 2017-07-25\n### Changed\n- Migrated to `start_ipython()` for a fully functional shell instead of using an embedded IPython instance.\n- Added support for passing CLI arguments to IPython.\n\n## [0.2.2] - 2016-12-06\n### Changed\n- Updated IPython version to `>=5.0.0` in dependencies.\n- Improved banner format to include IPython version.\n\n## [0.2.1] - 2016-07-08\n### Removed\n- Reverted support for `PYTHONSTARTUP` script handling.\n\n## [0.2.0] - 2016-07-08\n### Added\n- Added support for IPython `>=5.0.0`.\n- Introduced a more consistent banner format for the shell.\n\n## [0.1.1] - 2016-06-20\n### Fixed\n- Improved the help command text.\n- Added proper package metadata in `setup.py`.\n\n## [0.1.0] - 2016-06-20\n### Added\n- Initial release of `flask-shell-ipython`.\n- Replaces the default Flask shell command with IPython.\n\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2016 Andrew Grigorev\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Flask-Shell-IPython\n\n`flask-shell-ipython` is a Python package that replaces the default `flask\nshell` command with a similar command that runs IPython. This provides an\nenhanced interactive Python shell with additional features like syntax\nhighlighting, tab-completion, and more.\n\n## Installation\n\nTo install `flask-shell-ipython`, simply run:\n\n```bash\npip install flask-shell-ipython\n```\n\n## Usage\n\nAfter installing `flask-shell-ipython`, the `flask shell` command will\nautomatically use IPython instead of the default Python shell. There are no\nadditional steps required.\n\n```bash\nflask shell\n```\n\nYou can also pass any valid IPython arguments after the `flask shell` command:\n\n```bash\nflask shell --no-banner -i foo.py\n```\n\n## Configuration\n\nYou can configure IPython settings by adding an `IPYTHON_CONFIG` key to your\nFlask app's configuration. The value should be a dictionary containing the\nconfiguration options you'd like to set.\n\nFor example:\n\n```python\napp.config['IPYTHON_CONFIG'] = {\n    'InteractiveShell': {\n        'colors': 'Linux',\n        'confirm_exit': False,\n    },\n}\n```\n\n## Testing\n\nTo run tests for `flask-shell-ipython`, install the `pytest-forked` plugin,\nwhich enables running tests in isolated forked subprocesses to ensure running a\nclean IPython instance for each test case.\n\n### Installing Dependencies\n\nInstall testing dependencies from `requirements-test.txt`:\n\n```bash\npip install -r requirements-test.txt\n```\n\n### Running Tests\n\nAfter installing the dependencies, run the test suite with the `--forked` option:\n\n```bash\npytest --forked\n```\n\nPlease, note that does pytest-forked does not work on Windows. To test\nflask-shell-ipython on Windows run each test manually.\n\n## License\n\n`flask-shell-ipython` is licensed under the MIT License. See the\n[LICENSE](LICENSE) file for more information.\n\n## Contributing\n\nIf you'd like to contribute to the project, feel free to submit a pull request\non the GitHub repository at http://github.com/ei-grad/flask-shell-ipython.\n"
  },
  {
    "path": "flask_shell_ipython.py",
    "content": "import sys\n\nimport click\nfrom flask.cli import with_appcontext\n\n\n@click.command(context_settings=dict(ignore_unknown_options=True))\n@click.argument(\"ipython_args\", nargs=-1, type=click.UNPROCESSED)\n@with_appcontext\ndef shell(ipython_args):\n    \"\"\"Runs a shell in the app context.\n\n    Runs an interactive Python shell in the context of a given\n    Flask application. The application will populate the default\n    namespace of this shell according to its configuration.\n    This is useful for executing small snippets of management code\n    without having to manually configure the application.\n    \"\"\"\n    import IPython\n    from IPython.terminal.ipapp import load_default_config\n    from traitlets.config.loader import Config\n    from flask.globals import current_app as app\n\n    if \"IPYTHON_CONFIG\" in app.config:\n        config = Config(app.config[\"IPYTHON_CONFIG\"])\n    else:\n        config = load_default_config()\n\n    config.TerminalInteractiveShell.banner1 = f\"\"\"Python {sys.version} on {sys.platform}\nIPython: {IPython.__version__}\nApp: {app.import_name}{' [debug]' if app.debug else ''}\nInstance: {app.instance_path}\n\"\"\"\n\n    IPython.start_ipython(\n        argv=ipython_args,\n        user_ns=app.make_shell_context(),\n        config=config,\n    )\n"
  },
  {
    "path": "pyproject.toml",
    "content": "[build-system]\nrequires = [\"hatchling==1.25.0\"]\nbuild-backend = \"hatchling.build\"\n\n[project]\nname = \"flask-shell-ipython\"\nversion = \"0.5.3\"\ndescription = \"Replace default `flask shell` command by similar command running IPython.\"\nreadme = \"README.md\"\nreadme-content-type = \"text/markdown\"\nrequires-python = \">=3.8, <4.0\"\nlicense = \"MIT\"\n# expect `license-files` field to break on any new version of hatch, because\n# PEP-639 is already changed from this structure as of the time of writing\nlicense-files = { paths = [\"LICENSE\"] }\nauthors = [\n    { name = \"Andrew Grigorev\", email = \"andrew@ei-grad.ru\" }\n]\nclassifiers = [\n    \"Development Status :: 5 - Production/Stable\",\n    \"Environment :: Console\",\n    \"Framework :: Flask\",\n    \"Framework :: IPython\",\n    \"Intended Audience :: Developers\",\n    \"Programming Language :: Python :: 3\",\n]\n\ndependencies = [\n    \"Flask>=1.0\",\n    \"click\",\n    \"IPython>=5.0.0\"\n]\n\n[project.urls]\nHomepage = \"https://github.com/ei-grad/flask-shell-ipython\"\nChangelog = \"https://github.com/ei-grad/flask-shell-ipython/blob/main/CHANGELOG.md\"\n\n[project.entry-points.\"flask.commands\"]\nshell = \"flask_shell_ipython:shell\"\n\n[tool.hatch.build.targets.sdist]\nexclude = [\n  \".github\",\n]\n"
  },
  {
    "path": "requirements-test.txt",
    "content": "pytest\npytest-forked; platform_system != \"Windows\"\n"
  },
  {
    "path": "test_flask_shell_ipython.py",
    "content": "import pytest\nfrom click.testing import CliRunner\nfrom flask import Flask\n\nfrom flask_shell_ipython import shell\n\n\n@pytest.fixture\ndef app():\n    app = Flask(__name__)\n    app.config[\"TESTING\"] = True\n    return app\n\n\n@pytest.fixture\ndef runner(app):\n    return CliRunner()\n\n\ndef test_shell_command(runner, app):\n    with app.app_context():\n        result = runner.invoke(shell)\n    assert result.exit_code == 0\n    assert \"IPython\" in result.output\n\n\ndef test_shell_command_no_banner(runner, app):\n    with app.app_context():\n        result = runner.invoke(shell, [\"--no-banner\", \"--no-confirm-exit\"])\n    assert result.exit_code == 0\n    assert result.output == \"\\nIn [1]: \"\n\n\ndef test_shell_command_with_custom_config(runner, app):\n    app.config[\"IPYTHON_CONFIG\"] = {\n        \"InteractiveShell\": {\"confirm_exit\": False},\n        \"TerminalIPythonApp\": {\"display_banner\": False}\n    }\n    with app.app_context():\n        result = runner.invoke(shell)\n    assert result.exit_code == 0\n    assert result.output == \"\\nIn [1]: \"\n"
  }
]