Full Code of BradenM/micropy-cli for AI

master f361a0465c15 cached
165 files
418.7 KB
116.9k tokens
766 symbols
1 requests
Download .txt
Showing preview only (459K chars total). Download the full file or copy to clipboard to get everything.
Repository: BradenM/micropy-cli
Branch: master
Commit: f361a0465c15
Files: 165
Total size: 418.7 KB

Directory structure:
gitextract_7ldyguvi/

├── .chglog/
│   ├── CHANGELOG.tpl.md
│   └── config.yml
├── .editorconfig
├── .git-blame-ignore-revs
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   ├── config.yml
│   │   ├── feature_request.md
│   │   └── question.md
│   ├── actions/
│   │   └── setup-micropy/
│   │       └── action.yml
│   ├── codeql/
│   │   └── codeql-config.yml
│   ├── renovate.json5
│   └── workflows/
│       ├── changelog.yml
│       ├── codeql-analysis.yml
│       ├── main.yml
│       ├── publish.yml
│       └── release.yml
├── .gitignore
├── .pre-commit-config.yaml
├── .prettierignore
├── .readthedocs.yml
├── .release-please-manifest.json
├── .tool-versions
├── CHANGELOG.md
├── LICENSE
├── Makefile
├── README.md
├── docs/
│   ├── Makefile
│   ├── _autosummary/
│   │   ├── micropy.config.config_source.rst
│   │   ├── micropy.config.rst
│   │   ├── micropy.exceptions.rst
│   │   ├── micropy.main.rst
│   │   ├── micropy.packages.rst
│   │   ├── micropy.project.modules.rst
│   │   ├── micropy.project.rst
│   │   ├── micropy.rst
│   │   ├── micropy.stubs.rst
│   │   ├── micropy.stubs.source.rst
│   │   └── micropy.utils.rst
│   ├── base.md
│   ├── cli.rst
│   ├── conf.py
│   ├── header.rst
│   ├── index.rst
│   └── modules.rst
├── micropy/
│   ├── __init__.py
│   ├── __main__.py
│   ├── app/
│   │   ├── __init__.py
│   │   ├── main.py
│   │   └── stubs.py
│   ├── config/
│   │   ├── __init__.py
│   │   ├── config.py
│   │   ├── config_dict.py
│   │   ├── config_json.py
│   │   └── config_source.py
│   ├── data/
│   │   ├── __init__.py
│   │   ├── schemas/
│   │   │   ├── firmware.json
│   │   │   └── stubs.json
│   │   └── sources.json
│   ├── exceptions.py
│   ├── logger.py
│   ├── main.py
│   ├── packages/
│   │   ├── __init__.py
│   │   ├── package.py
│   │   ├── source.py
│   │   ├── source_package.py
│   │   └── source_path.py
│   ├── project/
│   │   ├── __init__.py
│   │   ├── checks.py
│   │   ├── modules/
│   │   │   ├── __init__.py
│   │   │   ├── modules.py
│   │   │   ├── packages.py
│   │   │   ├── stubs.py
│   │   │   └── templates.py
│   │   ├── project.py
│   │   ├── template/
│   │   │   ├── .gitignore
│   │   │   ├── .pylintrc
│   │   │   ├── .vscode/
│   │   │   │   ├── extensions.json
│   │   │   │   └── settings.json
│   │   │   ├── pymakr.conf
│   │   │   └── src/
│   │   │       ├── boot.py
│   │   │       └── main.py
│   │   └── template.py
│   ├── py.typed
│   ├── pyd/
│   │   ├── __init__.py
│   │   ├── abc.py
│   │   ├── backend_rshell.py
│   │   ├── backend_upydevice.py
│   │   ├── consumers.py
│   │   └── pydevice.py
│   ├── stubs/
│   │   ├── __init__.py
│   │   ├── manifest.py
│   │   ├── package.py
│   │   ├── repo.py
│   │   ├── repo_package.py
│   │   ├── repositories/
│   │   │   ├── __init__.py
│   │   │   ├── micropy.py
│   │   │   └── micropython.py
│   │   ├── repository_info.py
│   │   ├── source.py
│   │   └── stubs.py
│   └── utils/
│       ├── __init__.py
│       ├── _compat.py
│       ├── decorators.py
│       ├── helpers.py
│       ├── stub.py
│       ├── types.py
│       └── validate.py
├── pyproject.toml
├── release-please-config.json
├── scripts/
│   └── export-docs-reqs.sh
└── tests/
    ├── __init__.py
    ├── app/
    │   ├── conftest.py
    │   ├── test_main.py
    │   └── test_stubs.py
    ├── conftest.py
    ├── data/
    │   ├── esp32_test_stub/
    │   │   ├── frozen/
    │   │   │   ├── ntptime.py
    │   │   │   └── ntptime.pyi
    │   │   ├── info.json
    │   │   └── stubs/
    │   │       ├── machine.py
    │   │       └── modules.json
    │   ├── esp8266_invalid_stub/
    │   │   └── info.json
    │   ├── esp8266_test_stub/
    │   │   ├── frozen/
    │   │   │   ├── ntptime.py
    │   │   │   └── ntptime.pyi
    │   │   ├── info.json
    │   │   └── stubs/
    │   │       ├── machine.py
    │   │       └── modules.json
    │   ├── fware_test_stub/
    │   │   ├── frozen/
    │   │   │   ├── utarfile.py
    │   │   │   ├── utarfile.pyi
    │   │   │   ├── utokenize.py
    │   │   │   └── utokenize.pyi
    │   │   └── info.json
    │   ├── project_test/
    │   │   ├── .pylintrc
    │   │   ├── .vscode/
    │   │   │   └── settings.json
    │   │   └── micropy.json
    │   ├── stubber_test_stub/
    │   │   ├── micropython.py
    │   │   └── modules.json
    │   ├── test_repo.json
    │   ├── test_source.xml
    │   └── test_sources.json
    ├── test_checks.py
    ├── test_config.py
    ├── test_highlevel.py
    ├── test_main.py
    ├── test_packages.py
    ├── test_project.py
    ├── test_pyd.py
    ├── test_stub_source.py
    ├── test_stubs/
    │   ├── bad_test_stub/
    │   │   └── modules.json
    │   ├── esp32_test_stub/
    │   │   ├── frozen/
    │   │   │   ├── ntptime.py
    │   │   │   └── ntptime.pyi
    │   │   ├── info.json
    │   │   └── stubs/
    │   │       ├── machine.py
    │   │       └── modules.json
    │   └── esp8266_test_stub/
    │       ├── frozen/
    │       │   ├── ntptime.py
    │       │   └── ntptime.pyi
    │       ├── info.json
    │       └── stubs/
    │           ├── machine.py
    │           └── modules.json
    ├── test_stubs.py
    ├── test_stubs_repo.py
    ├── test_template.py
    ├── test_utils/
    │   ├── fail.json
    │   ├── pass.json
    │   └── schema.json
    └── test_utils.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .chglog/CHANGELOG.tpl.md
================================================
{{ if .Versions -}}
<a name="unreleased"></a>
## [Unreleased]

{{ if .Unreleased.CommitGroups -}}
{{ range .Unreleased.CommitGroups -}}
### {{ .Title }}
{{ range .Commits -}}
- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }}
{{ end }}
{{ end -}}
{{ end -}}
{{ end -}}

{{ range .Versions }}
<a name="{{ .Tag.Name }}"></a>
## {{ if .Tag.Previous }}[{{ .Tag.Name }}]{{ else }}{{ .Tag.Name }}{{ end }} - {{ datetime "2006-01-02" .Tag.Date }}
{{ range .CommitGroups -}}
### {{ .Title }}
{{ range .Commits -}}
- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }}
{{ end }}
{{ end -}}

{{- if .RevertCommits -}}
### Reverts
{{ range .RevertCommits -}}
- {{ .Revert.Header }}
{{ end }}
{{ end -}}

{{- if .MergeCommits -}}
### Pull Requests
{{ range .MergeCommits -}}
- {{ .Header }}
{{ end }}
{{ end -}}

{{- if .NoteGroups -}}
{{ range .NoteGroups -}}
### {{ .Title }}
{{ range .Notes }}
{{ .Body }}
{{ end }}
{{ end -}}
{{ end -}}
{{ end -}}

{{- if .Versions }}
[Unreleased]: {{ .Info.RepositoryURL }}/compare/{{ $latest := index .Versions 0 }}{{ $latest.Tag.Name }}...HEAD
{{ range .Versions -}}
{{ if .Tag.Previous -}}
[{{ .Tag.Name }}]: {{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }}
{{ end -}}
{{ end -}}
{{ end -}}


================================================
FILE: .chglog/config.yml
================================================
style: github
template: CHANGELOG.tpl.md
info:
  title: CHANGELOG
  repository_url: https://github.com/BradenM/micropy-cli
options:
  commits:
    filters:
      Type:
        - feat
        - fix
        - perf
        - refactor
  commit_groups:
    title_maps:
      feat: Features
      fix: Bug Fixes
      perf: Performance Improvements
      refactor: Code Refactoring
  header:
    pattern: "^(\\w*)(?:\\(([\\w\\$\\.\\-\\*\\s]*)\\))?\\:\\s(.*)$"
    pattern_maps:
      - Type
      - Scope
      - Subject

  issues:
    prefix:
      -  #

  refs:
    actions:
      - Closes
      - Fixes

  merges:
    pattern: "^Merge branch '(\\w+)'$"
    pattern_maps:
      - Source

  reverts:
    pattern: "^Revert \"([\\s\\S]*)\"$"
    pattern_maps:
      - Header

  notes:
    keywords:
      - BREAKING CHANGE


================================================
FILE: .editorconfig
================================================
# http://editorconfig.org

root = true

[*]
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
insert_final_newline = true
charset = utf-8
end_of_line = lf

[LICENSE]
insert_final_newline = false

[Makefile]
indent_style = tab

[*.py]
profile = black

[*.y{,a}ml]
indent_size = 2


================================================
FILE: .git-blame-ignore-revs
================================================
# Format all files with pre-commit->black, isort, autoflake and other hooks
80755a39f3fbd9983a07ff4571197b8f6dcae1cb
# style(pyupgrade): format all files
402f8e68ee9c211e1f0238b8531b3ee0a9ce6806
# style: format all with new pre-commit rules.
0d679f27331c2ba851541c5d0ab34fe04923e660


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve Micropy Cli
title: ''
labels: bug
assignees: ''

---

<!--- Found a bug? Thanks for letting us know! -->

<!--- Provide a general summary of the issue in the Title above -->

**Describe the bug**
<!--- Tell us what the bug is. -->

**Expected Behavior**
<!--- Tell us what you expect/should happen. -->

**Current Behavior**
<!--- Tell us what happens instead of the expected behavior -->

**Steps to Reproduce**
<!--- Provide a link to a live example, or an unambiguous set of steps to -->
<!--- reproduce this bug. Include code to reproduce, if relevant -->

1.
2.
3.


**Possible Solution**
<!--- Not obligatory, but suggest a fix/reason for the bug -->
<!-- Feel free to delete if not applicable. -->


**Logs**
<!-- MicropyCli's log file can be found under $HOME/.micropy/micropy.log -->
<!-- Please attach this file or provide the output relative to your issue -->
<!-- using markdown ```<content>``` code tags. -->


**Context (Environment)**
<!-- Please provide the following information (if applicable) -->
<!-- Explanations for each item can be found below -->

<!-- OS? -->
<!-- Windows/Linux/MacOS/etc, examples: Windows 10 or GNU/Linux 5.4.3-1-MANJARO -->

<!-- Micropy Version? -->
<!-- Can be found by running: micropy --version -->

<!-- Python Version? -->
<!-- Current Python version. -->

<!-- VSCode Version? -->
<!-- Current VSCode version. (If applicable)  -->

* OS:
* Micropy Version:
* Python Version:
* VSCode Version:


================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: true
contact_links:
    - name: Micropy Stubs
      url: https://github.com/BradenM/micropy-stubs
      about: Module missing or can't find your device? Make an issue on micropy-stubs.


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''

---

<!-- Got a feature you'd like to see implemented? Let us know! -->

**Is your feature request related to a problem? Please describe.**
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->

**Describe the solution you'd like**
<!-- A clear and concise description of what you want to happen. -->

**Describe alternatives you've considered**
<!-- A clear and concise description of any alternative solutions or features you've considered. -->

**Additional context**
<!-- Add any other context or screenshots about the feature request here. -->


================================================
FILE: .github/ISSUE_TEMPLATE/question.md
================================================
---
name: Question
about: Got a question? Ask it!
title: ''
labels: question
assignees: ''

---


================================================
FILE: .github/actions/setup-micropy/action.yml
================================================
name: "Setup Micropy"
description: "Setup micropy CI env."
inputs:
  poetry-version:
    description: Poetry version to use.
    required: true
  poetry-install-url:
    description: Poetry install url to use.
    required: false
    default: "https://install.python-poetry.org"
  poetry-home:
    description: Path to use as POETRY_HOME
    required: false
    default: "/tmp/opt/poetry"
  python-version:
    description: Python version to use.
    required: true
  runner:
    description: Explicit runner cache to use.
    required: false
    default: ""
runs:
  using: composite
  steps:
    # See: https://github.com/actions/cache/blob/main/workarounds.md#improving-cache-restore-performance-on-windowsusing-cross-os-caching
    - name: Use GNU tar
      if: runner.os == 'Windows'
      shell: cmd
      run: |
        echo "Adding GNU tar to PATH"
        echo C:\Program Files\Git\usr\bin>>"%GITHUB_PATH%"

    - name: Setup Poetry env.
      shell: bash
      run: |
        echo 'POETRY_HOME=${{ inputs.poetry-home }}' >> $GITHUB_ENV
        echo 'POETRY_VIRTUALENVS_IN_PROJECT=true' >> $GITHUB_ENV
        echo 'POETRY_NO_INTERACTION=true' >> $GITHUB_ENV
        echo 'POETRY_VERSION=${{ inputs.poetry-version }}' >> $GITHUB_ENV
        echo "${{ inputs.poetry-home }}/bin" >> $GITHUB_PATH
        echo "$HOME/.local/bin" >> $GITHUB_PATH

    - name: Setup Win Path
      if: runner.os == 'Windows'
      shell: pwsh
      run: |
        echo "C:\Users\runneradmin\AppData\Local\Temp\opt\poetry\bin" | Out-File -FilePath $env:GITHUB_PATH -Append
        echo "C:\Users\runneradmin\AppData\Local\Temp\opt\poetry\venv\Scripts" | Out-File -FilePath $env:GITHUB_PATH -Append

    - name: Workaround Poetry v1.4.0 Windows issues.
      if: runner.os == 'Windows'
      shell: bash
      run: |
        # have not looked into why this occurs.
        # just disable new installer for windows.
        echo 'POETRY_INSTALLER_MODERN_INSTALLATION=false' >> $GITHUB_ENV

    - name: Cache poetry install.
      uses: actions/cache@v3
      id: poetry-install-cache
      with:
        path: ${{ inputs.poetry-home }}/install-poetry.py
        key: poetry-install-${{ inputs.runner || matrix.os || runner.os }}-${{ inputs.poetry-version }}

    - name: Fetch Poetry Installer
      shell: bash
      if: steps.poetry-install-cache.outputs.cache-hit != 'true'
      run: |
        mkdir -p "${{ inputs.poetry-home }}"
        curl -sSL -o ${{ inputs.poetry-home }}/install-poetry.py ${{ inputs.poetry-install-url }}

    - name: Set up Python ${{ inputs.python-version }}
      uses: actions/setup-python@v4
      id: python-setup
      with:
        python-version: ${{ inputs.python-version }}

    - name: Install Poetry
      shell: bash
      run: |
        python ${{ inputs.poetry-home }}/install-poetry.py --version ${{ inputs.poetry-version }}
        ${{ inputs.poetry-home }}/bin/poetry --version

    - name: Get poetry cache dir.
      id: poetry-config
      shell: bash
      run: |
        POETRY="${{ inputs.poetry-home }}/bin/poetry"
        CACHE_DIR="$($POETRY config cache-dir)"
        echo "Poetry cache: $CACHE_DIR"
        echo "cache-dir=$CACHE_DIR" >> $GITHUB_OUTPUT

    - name: Cache poetry cache.
      uses: actions/cache@v3
      with:
        path: ${{ steps.poetry-config.outputs.cache-dir }}
        key: poetry-cache-${{ inputs.runner || matrix.os || runner.os }}-${{ inputs.python-version }}-${{ inputs.poetry-version }}

    - name: Cache virtual env.
      uses: actions/cache@v3
      id: venv-cache
      with:
        path: .venv
        key: poetry-venv-${{ inputs.runner || matrix.os || runner.os }}-${{ inputs.python-version }}-${{ inputs.poetry-version }}-${{ hashFiles('poetry.lock') }}
        restore-keys: |
          poetry-venv-${{ inputs.runner || matrix.os || runner.os }}-${{ inputs.python-version }}-${{ inputs.poetry-version }}-

    - name: Install dependencies.
      shell: bash
      if: steps.venv-cache.outputs.cache-hit != 'true'
      run: |
        ${{ inputs.poetry-home }}/bin/poetry install --with docs --with test -v
        ${{ inputs.poetry-home }}/bin/poetry env info


================================================
FILE: .github/codeql/codeql-config.yml
================================================
# do not scan entire .venv
# security alters stemming from dependencies are handled elsewhere (dependabot alerts+github).
paths-ignore:
  - .venv
  - test


================================================
FILE: .github/renovate.json5
================================================
{
    $schema: "https://docs.renovatebot.com/renovate-schema.json",
    extends: [
        "config:base",
        ":rebaseStalePrs",
        ":prConcurrentLimit20",
        ":prHourlyLimitNone",
        ":pinDependencies",
        ":automergeMinor",
        ":automergeDigest",
    ],
    addLabels: ["dependencies"],
    major: {
        automerge: false,
    },
    ignorePaths: ["docs/requirements.txt"],
    dependencyDashboard: true,
    packageRules: [
        {
            matchDepTypes: ["devDependencies"],
            matchUpdateTypes: ["minor", "patch"],
            automerge: true,
            groupName: "devDependencies (non-major)"
        },
        {
            matchDepTypes: ["devDependencies"],
            matchUpdateTypes:  ["major"],
            automerge: true
        }
    ],
}


================================================
FILE: .github/workflows/changelog.yml
================================================
name: Changelog

on:
  push:
    branches:
      - master

jobs:
  changelog:
    name: Generate Changelog
    runs-on: ubuntu-latest
    continue-on-error: true
    if: github.actor != 'github-actions[bot]' || github.event.pusher.email != 'github-actions[bot]@users.noreply.github.com'
    steps:
      - uses: actions/checkout@v3
        with:
          token: ${{ secrets.GH_PAT }}
          fetch-depth: 0

      - name: Setup go
        uses: actions/setup-go@v3

      - name: Generate Changelog
        run: go run github.com/git-chglog/git-chglog/cmd/git-chglog@latest --output CHANGELOG.md

      - name: Commit Changes
        run: |
          git config --local user.email "github-actions[bot]@users.noreply.github.com"
          git config --local user.name "github-actions[bot]"
          git commit -m "chore(chglog): update changelog." -a || true

      - name: Push Changes
        uses: ad-m/github-push-action@77c5b412c50b723d2a4fbc6d71fb5723bcd439aa
        with:
          github_token: ${{ secrets.GH_PAT }}
          branch: ${{ github.ref }}


================================================
FILE: .github/workflows/codeql-analysis.yml
================================================
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"

on:
  push:
    branches: [master]
  pull_request:
    # The branches below must be a subset of the branches above
    branches: [master]
  schedule:
    - cron: "24 9 * * 4"

env:
  POETRY_VERSION: 1.8.3
  PYTHON_VERSION: 3.11

jobs:
  analyze:
    name: Analyze
    runs-on: ubuntu-latest
    permissions:
      actions: read
      contents: read
      security-events: write

    strategy:
      fail-fast: false
      matrix:
        language: ["python"]
        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
        # Learn more about CodeQL language support at https://git.io/codeql-language-support

    steps:
      - name: Checkout repository
        uses: actions/checkout@v3

      - name: Setup environment.
        uses: ./.github/actions/setup-micropy
        with:
          poetry-version: ${{ env.POETRY_VERSION }}
          python-version: ${{ env.PYTHON_VERSION }}
          runner: ubuntu-latest

      # Initializes the CodeQL tools for scanning.
      - name: Initialize CodeQL
        uses: github/codeql-action/init@v2
        env:
          CODEQL_PYTHON: ./.venv/bin/python
        with:
          languages: ${{ matrix.language }}
          setup-python-dependencies: false
          config-file: ./.github/codeql/codeql-config.yml
          source-root: micropy
          # If you wish to specify custom queries, you can do so here or in a config file.
          # By default, queries listed here will override any specified in a config file.
          # Prefix the list here with "+" to use these queries and those in the config file.
          # queries: ./path/to/local/query, your-org/your-repo/queries@main

      - name: Perform CodeQL Analysis
        env:
          CODEQL_PYTHON: ./.venv/bin/python
        uses: github/codeql-action/analyze@v2


================================================
FILE: .github/workflows/main.yml
================================================
name: Test MicropyCli

on:
  pull_request: ~
  push:
    branches:
      - master

env:
  POETRY_VERSION: 1.8.3

concurrency:
  group: main-${{ github.event_name }}-${{ github.ref }}
  cancel-in-progress: true

defaults:
  run:
    shell: bash

jobs:
  test:
    name: ${{ matrix.os }} @ Py v${{ matrix.python }}
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        # explicitly use macOS-12 to avoid
        # macOS-11 cached wheels failing to install on a 12 runner.
        # this is due to an active transition by github.
        # see: https://github.blog/changelog/2022-10-03-github-actions-jobs-running-on-macos-latest-are-now-running-on-macos-12/
        os: [windows-latest, macOS-12, ubuntu-latest]
        python: ["3.9", "3.10", "3.11"]

    steps:
      - uses: actions/checkout@v3
        with:
          submodules: true

      - name: Setup environment.
        uses: ./.github/actions/setup-micropy
        with:
          poetry-version: ${{ env.POETRY_VERSION }}
          python-version: ${{ matrix.python }}

      - name: Run Tests
        run: poetry run pytest --cov --cov-config=pyproject.toml --junit-xml=test_log.xml --cov-report=xml:cov.xml -vv -ra -n'auto'

      - name: Upload Codecov
        uses: codecov/codecov-action@v3
        env:
          OS: ${{ matrix.os }}
          PYTHON: ${{ matrix.python }}
        with:
          files: ./cov.xml
          fail_ci_if_error: false
          flags: unittests,py-${{ matrix.python }},os-${{ matrix.os }}
          env_vars: OS,PYTHON


================================================
FILE: .github/workflows/publish.yml
================================================
name: Publish Release

on:
  release:
    types:
      - created

env:
  POETRY_VERSION: 1.8.3
  PYTHON_VERSION: 3.11

jobs:
  publish:
    name: Publish
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          submodules: true

      - name: Setup environment.
        uses: ./.github/actions/setup-micropy
        with:
          poetry-version: ${{ env.POETRY_VERSION }}
          python-version: ${{ env.PYTHON_VERSION }}

      - name: Build
        run: poetry build

      - name: Publish to PyPi
        env:
          POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_TOKEN }}
        run: poetry publish


================================================
FILE: .github/workflows/release.yml
================================================
name: Release

on:
  workflow_dispatch:
  push:
    branches:
      - master

jobs:
  release-please:
    name: Release Please
    runs-on: ubuntu-latest
    steps:
      - name: Release Please
        id: release-please
        # see: googleapis/release-please#1837
        uses: BradenM/release-please-action@d0fa220390843191f01153795b2e5dce67410563
        with:
          token: ${{ secrets.GH_PAT }}
          command: manifest


================================================
FILE: .gitignore
================================================

# Created by https://www.gitignore.io/api/python
# Edit at https://www.gitignore.io/?templates=python

### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
cov.xml
test_log.xml
.testmondata
.tmontmp/


# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pipenv
#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
#   However, in case of collaboration, if having platform-specific dependencies or dependencies
#   having no cross-platform support, pipenv may install dependencies that don’t work, or not
#   install all needed dependencies.
Pipfile.lock

# celery beat schedule file
celerybeat-schedule

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
.envrc

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# End of https://www.gitignore.io/api/python

.vscode
!.vscode/tasks.json
!micropy/project/template/**/*
!micropy/lib
!tests/data/project_test/**/*

# Created by https://www.toptal.com/developers/gitignore/api/pycharm+all
# Edit at https://www.toptal.com/developers/gitignore?templates=pycharm+all

### PyCharm+all ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839

# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf

# Generated files
.idea/**/contentModel.xml

# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml

# Gradle
.idea/**/gradle.xml
.idea/**/libraries

# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn.  Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr

# CMake
cmake-build-*/

# Mongo Explorer plugin
.idea/**/mongoSettings.xml

# File-based project format
*.iws

# IntelliJ
out/

# mpeltonen/sbt-idea plugin
.idea_modules/

# JIRA plugin
atlassian-ide-plugin.xml

# Cursive Clojure plugin
.idea/replstate.xml

# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties

# Editor-based Rest Client
.idea/httpRequests

# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser

### PyCharm+all Patch ###
# Ignores the whole .idea folder and all .iml files
# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360

.idea/

# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023

*.iml
modules.xml
.idea/misc.xml
*.ipr

# Sonarlint plugin
.idea/sonarlint

# End of https://www.toptal.com/developers/gitignore/api/pycharm+all

temp/*

# Created by https://www.toptal.com/developers/gitignore/api/direnv
# Edit at https://www.toptal.com/developers/gitignore?templates=direnv

### direnv ###
.direnv
.envrc

# End of https://www.toptal.com/developers/gitignore/api/direnv


================================================
FILE: .pre-commit-config.yaml
================================================
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
exclude: |
  (?x)^(
    /(
        \.eggs
      | \.git
      | \.hg
      | \.mypy_cache
      | \.tox
      | \.venv
      | _build
      | build
      | dist
      | micropy/lib
    )/
    | foo.py
  )$

repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.4.0
    hooks:
      - id: debug-statements
      - id: detect-private-key
      - id: end-of-file-fixer
      - id: check-executables-have-shebangs
  - repo: https://github.com/charliermarsh/ruff-pre-commit
    rev: "v0.0.255"
    hooks:
      - id: ruff
        args:
          - --fix
  - repo: https://github.com/pycqa/isort
    rev: 5.12.0
    hooks:
      - id: isort
  - repo: https://github.com/psf/black
    rev: 23.1.0
    hooks:
      - id: black
  - repo: https://github.com/python-poetry/poetry
    rev: 1.4.1
    hooks:
      - id: poetry-check
        files: "^(pyproject.toml|poetry.lock)$"

ci:
  autofix_commit_msg: "ci(pre-commit.ci): 🎨 Auto format from pre-commit.com hooks"
  autoupdate_commit_msg: "ci(pre-commit.ci): ⬆ pre-commit autoupdate"


================================================
FILE: .prettierignore
================================================
micropy/template/**


================================================
FILE: .readthedocs.yml
================================================
# .readthedocs.yml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details

# Required
version: 2

# Build documentation in the docs/ directory with Sphinx
sphinx:
  configuration: docs/conf.py

# Fetch Submodules
submodules:
  include: all

# Optionally build your docs in additional formats such as PDF and ePub
formats: all

build:
  os: ubuntu-22.04
  tools:
    python: "3.11"
  jobs:
    # see: https://docs.readthedocs.io/en/stable/build-customization.html#install-dependencies-with-poetry
    post_create_environment:
      - pip install poetry==1.4.1
      - poetry config virtualenvs.create false
    post_install:
      - poetry install --with docs


================================================
FILE: .release-please-manifest.json
================================================
{
    ".": "4.2.2"
}


================================================
FILE: .tool-versions
================================================
python     3.11.5
poetry     1.8.3
git-chglog 0.15.2


================================================
FILE: CHANGELOG.md
================================================
<a name="v4.0.0"></a>

## [4.2.2](https://github.com/BradenM/micropy-cli/compare/v4.2.1...v4.2.2) (2023-06-14)


### Bug Fixes

* **cli:** Re-add version as command ([6632c5b](https://github.com/BradenM/micropy-cli/commit/6632c5bbe82f5ebf6f306c5fbe500213d208a0fb))
* **deps:** Update dependency libcst to v0.4.10 ([5b33886](https://github.com/BradenM/micropy-cli/commit/5b33886eb6210231a7afc4be5920f7b7c92fd762))
* **deps:** Update dependency markupsafe to v2.1.3 ([2b5e7a3](https://github.com/BradenM/micropy-cli/commit/2b5e7a38f53cc7605a310187817a9a85a11b7624))
* **deps:** Update dependency pydantic to v1.10.8 ([3672fcf](https://github.com/BradenM/micropy-cli/commit/3672fcf6997647366abea9672c102dc24bc5bf77))
* **deps:** Update dependency pydantic to v1.10.9 ([66fa6e5](https://github.com/BradenM/micropy-cli/commit/66fa6e5147b29183c09dadfd4dd65e7956111596))
* **deps:** Update dependency requests to v2.31.0 ([352d3b3](https://github.com/BradenM/micropy-cli/commit/352d3b3ad09c0723c7b9572245c336ccac22afb7))
* **deps:** Update dependency typing-extensions to v4.6.0 ([4858007](https://github.com/BradenM/micropy-cli/commit/4858007c327a8a258196ecbca4fe4507da90332b))
* **deps:** Update dependency typing-extensions to v4.6.1 ([24c16f2](https://github.com/BradenM/micropy-cli/commit/24c16f2978f08a6e2ac4387354ed7e309ff2158c))
* **deps:** Update dependency typing-extensions to v4.6.2 ([98ae691](https://github.com/BradenM/micropy-cli/commit/98ae691da95de484e9dd68e0a13f79400755168c))
* **deps:** Update dependency typing-extensions to v4.6.3 ([dd993e5](https://github.com/BradenM/micropy-cli/commit/dd993e517bab238d01aa11d03bb79aac2fe50020))

## [4.2.1](https://github.com/BradenM/micropy-cli/compare/v4.2.0...v4.2.1) (2023-05-18)


### Bug Fixes

* **deps:** Update dependency attrs to v23 ([3422a84](https://github.com/BradenM/micropy-cli/commit/3422a84f576e2db7e39331512e71e3d72ed85d55))
* **deps:** Update dependency python-minifier to v2.9.0 ([2a7464c](https://github.com/BradenM/micropy-cli/commit/2a7464cf9be34dc4766083fab6570fe0c507e5d7))
* **deps:** Update dependency requests to v2.29.0 ([8361e7c](https://github.com/BradenM/micropy-cli/commit/8361e7cc15b0bdcefa07048814f30c0d4bb531b3))
* **deps:** Update dependency requests to v2.30.0 ([9e389b1](https://github.com/BradenM/micropy-cli/commit/9e389b11c171d16af26b28217993e38bbbf4fa4f))
* **deps:** Update dependency typer to v0.8.0 ([d2bf4e6](https://github.com/BradenM/micropy-cli/commit/d2bf4e69e83da62b589658ed3210742152547db6))
* **deps:** Update dependency typer to v0.9.0 ([f8f4105](https://github.com/BradenM/micropy-cli/commit/f8f4105e65370eb275114f1e30ce5c3361e61dd5))
* **stubs:** Always ensure correct pyi stub root paths. ([27f87a1](https://github.com/BradenM/micropy-cli/commit/27f87a1d697e2ee5e18d178eb46c7532b3c2fe29))
* **stubs:** Do not drop firmware name when parsing from dist metadata. ([be269ee](https://github.com/BradenM/micropy-cli/commit/be269ee5e241d9274d100c5dc1c9f28278b0d1d6))
* **template:** Resolve pylance type-checking / import errors. ([179c29d](https://github.com/BradenM/micropy-cli/commit/179c29d68bf025c1b3436d53dd3f9168d93285cf))
* **template:** Update pylint config to use `MAIN` and `INFERENCE` confidence level. ([f5f5c98](https://github.com/BradenM/micropy-cli/commit/f5f5c983cbeeb6730c85750ac78115d86135d44b))

## [4.2.0](https://github.com/BradenM/micropy-cli/compare/v4.2.0-beta.3...v4.2.0) (2023-04-22)


### Features

* **app:** Add exclude_defaults, improve help in stubs create command. ([efa3263](https://github.com/BradenM/micropy-cli/commit/efa3263121704e56d6e4dc0aa4975228b4731184))


### Documentation

* **app:** Improve stubs create help. ([9738ac7](https://github.com/BradenM/micropy-cli/commit/9738ac71256e8375ee190f3114ef5cb7e46c3506))


### Miscellaneous Chores

* **release:** Release as 4.2.0 ([817c583](https://github.com/BradenM/micropy-cli/commit/817c583bc460e642ddad0b383d88483945ee234b))

## [4.2.0-beta.3](https://github.com/BradenM/micropy-cli/compare/v4.2.0-beta.2...v4.2.0-beta.3) (2023-04-17)


### Features

* **app:** Expose compile, module-defaults flags, integrity in stubs create command. ([885e9d3](https://github.com/BradenM/micropy-cli/commit/885e9d369cad98345e6132315f6fb76694379339))
* **exc:** `PyDeviceFileIntegrityError` exception. ([a2187c8](https://github.com/BradenM/micropy-cli/commit/a2187c898c9aeb641a63a9237d2469faaf929de5))
* **pyd:** `NoOpConsumer` implementation. ([5a21ecd](https://github.com/BradenM/micropy-cli/commit/5a21ecd1e85f74a5976aac209457d8273d8596cb))
* **pydevice:** File integrity support, simple run in pydevice. ([8b7a255](https://github.com/BradenM/micropy-cli/commit/8b7a2557d42cb70430430ad04280dffebd45aeb6))
* **pyd:** File integrity check support in upydevice backend. ([a3cbf31](https://github.com/BradenM/micropy-cli/commit/a3cbf31f8ff5fd5f0da24661a911f030672d93b7))
* **pyd:** Implement `remove` across backends. ([f0bba8d](https://github.com/BradenM/micropy-cli/commit/f0bba8d25cd7eb6bdb2567bfde97c22a02088c16))
* **pyd:** Use noop consumer default for upy `eval`/`eval_script` ([720ace4](https://github.com/BradenM/micropy-cli/commit/720ace4355d5e7a794e6d204a6bccfe0249d5f5d))
* **utils:** Support compiling createstubs with mpy-cross ([1819197](https://github.com/BradenM/micropy-cli/commit/181919714c6d5d9a5a382e673bd81334164cbb53))


### Bug Fixes

* **deps:** Pin dependency typer to 0.7.0 ([30ab97f](https://github.com/BradenM/micropy-cli/commit/30ab97fc450ae5af9f58cf3e5770ce609cba9745))
* **pyd:** Remove usedforsecurity hash flag for py3.8 ([522a0c3](https://github.com/BradenM/micropy-cli/commit/522a0c3537634950178178628d097944ce554cb3))
* **pyd:** Support pushing binary files to pydevice in upydevice backend. ([e58c1f2](https://github.com/BradenM/micropy-cli/commit/e58c1f2f90f6e901298dfe54d0b5234818107140))


### Miscellaneous Chores

* **release:** Set release to v4.2.0-beta3 ([9ee81a2](https://github.com/BradenM/micropy-cli/commit/9ee81a2f4f63de78716b73b2bc4224a50e58a2c4))

## [4.2.0-beta.2](https://github.com/BradenM/micropy-cli/compare/v4.2.0-beta.1...v4.2.0-beta.2) (2023-03-27)


### Bug Fixes

* **deps:** Use latest stable typer. ([ec3082e](https://github.com/BradenM/micropy-cli/commit/ec3082ed541b724743f8bc7b5bb19ecce9c9e2bc))

## [4.2.0-beta.1](https://github.com/BradenM/micropy-cli/compare/v4.2.0-beta...v4.2.0-beta.1) (2023-03-27)


### Features

* **app:** Extract main cli logic out to command/option callbacks. ([e3f6401](https://github.com/BradenM/micropy-cli/commit/e3f6401f99faebaec13946b371b966d22edd681b)), closes [#338](https://github.com/BradenM/micropy-cli/issues/338)
* **app:** Implement main app cli entries with typer. ([d2b22a5](https://github.com/BradenM/micropy-cli/commit/d2b22a567a0e0e084378f0279aa23f5802fa8bc6))
* **app:** Implement stubs subcommand with typer. ([7733f24](https://github.com/BradenM/micropy-cli/commit/7733f243c62065500be46b5995819076c41fae32))
* **app:** Link to Josverl/micropython-stubber in create help ([88fb8dd](https://github.com/BradenM/micropy-cli/commit/88fb8dd8a90d120483ae008e1d46e553293032c6))
* **cli:** Remove old cli module. ([8e14fa6](https://github.com/BradenM/micropy-cli/commit/8e14fa691158a4c537cd89d39db1b92cd208bc42))
* **deps:** Add rich, typer, shellingham ([62f7c72](https://github.com/BradenM/micropy-cli/commit/62f7c729442399038b326628fa5c96c26b0e84a3))
* **deps:** Remove pytest-clarity ([d712580](https://github.com/BradenM/micropy-cli/commit/d71258076ed85343a5a145418e5dab11f446b85e))
* **dev:** Add and utilize external mock with pytest-mock ([79b3d96](https://github.com/BradenM/micropy-cli/commit/79b3d965f46a4ffd1e9bb972267f80ae77b68fda))
* **dev:** Add pdbpp to dev deps. ([239cb3e](https://github.com/BradenM/micropy-cli/commit/239cb3ebc690fd83583d4126cecae7268aea7c4e))
* **main:** Allow override of primary data directories. ([1689bfd](https://github.com/BradenM/micropy-cli/commit/1689bfdca7c8dcdb3b4cf137978c51a2ee33506e))
* **main:** Remove stub creation logic from micropy main state. ([1f8d9ba](https://github.com/BradenM/micropy-cli/commit/1f8d9baf32e742757f8bef348e90b289317cf9b4))
* **utils:** Remove import catch for stubber. ([91103e6](https://github.com/BradenM/micropy-cli/commit/91103e6e790954648ccf6d7f50d5049bfc477bfa))


### Bug Fixes

* **app:** Use future annotations. ([450c27d](https://github.com/BradenM/micropy-cli/commit/450c27d9f1b6748464715e888144eca9d0ac2c62))
* **compat:** Typer list type errors on py3.8 ([3483302](https://github.com/BradenM/micropy-cli/commit/34833023e9c80e74b02cea1d51447a6b203064b7))
* **compat:** Use typing.Type in app.stubs ([00858c2](https://github.com/BradenM/micropy-cli/commit/00858c292d4a098b4414138842a97761384cbaef))
* **deps:** Exclude pdbpp on windows ([445455e](https://github.com/BradenM/micropy-cli/commit/445455e07917cc1b7f647bc7cb89a65050a2ade9))
* **deps:** Update dependency pydantic to v1.10.7 ([36e4727](https://github.com/BradenM/micropy-cli/commit/36e47271a1da50a9c1a35646b0f82011781bf721))


### Miscellaneous Chores

* **release:** Set release to v4.2.0-beta.1 ([614d3aa](https://github.com/BradenM/micropy-cli/commit/614d3aac359373d929b113874d7d73976a2cbb3f))

## [4.2.0-beta](https://github.com/BradenM/micropy-cli/compare/v4.1.0...v4.2.0-beta) (2023-03-20)


### Features

* **cli:** Expose backend option to select pydevice backend. ([43f3751](https://github.com/BradenM/micropy-cli/commit/43f3751620acd6fcb49d0e4cc63d9afd4b998857))
* **deps:** Add libcst as dependency, remove py38 constraint from ([9bdb811](https://github.com/BradenM/micropy-cli/commit/9bdb811a2be6c2258f9e3619b9a9265565fe5ab7))
* **deps:** Add lint dependency group, remove unused/replaced with ([f820baa](https://github.com/BradenM/micropy-cli/commit/f820baae961b41d3386f1823565709be67436df0))
* **deps:** Add micropython-stubber as proper library. ([d82f5fc](https://github.com/BradenM/micropy-cli/commit/d82f5fcc49f3adc3c25a1eea010091f48ebbe056))
* **dx:** Replace pyupgrade/autoflake hooks with ruff ([3c47ebd](https://github.com/BradenM/micropy-cli/commit/3c47ebdee44547c2f3370fb18f6b6ef0ba5b7617))
* **lib:** Remove old micropython-stubber submodule. ([ea1ee8c](https://github.com/BradenM/micropy-cli/commit/ea1ee8cdebc8ed2e7be593412e069fd1c1a2fd39))
* **main:** Support create stubs backend parameter, utilize create stub variant. ([5cb26c3](https://github.com/BradenM/micropy-cli/commit/5cb26c38e06606b84f515ac7f8f0506ddf057e8b))
* **pkg:** Drop support for python 3.7 ([87eb790](https://github.com/BradenM/micropy-cli/commit/87eb7901fe7d0ba5cd974629fadbb82524603af7))
* **utils:** Prepare create stubs with codemod variants/modules, update stubmaker imports. ([2b8de82](https://github.com/BradenM/micropy-cli/commit/2b8de8298d5220f301b792f0718e74be5429904c))


### Bug Fixes

* **deps:** Pin dependencies ([9a7f407](https://github.com/BradenM/micropy-cli/commit/9a7f407e39be33a639c48de3527d6482ece82f26))
* **deps:** Remove pypi-test sourced from pyproject ([b3fc9e3](https://github.com/BradenM/micropy-cli/commit/b3fc9e3b09002c5d5c17fe28173055cc3d3d830e))
* **deps:** Target isort &lt;5.12.0 when on py3.7 ([f936752](https://github.com/BradenM/micropy-cli/commit/f936752a73898e9dce3ef5f3f4763384db0eab79))
* **deps:** Target pylint &lt;2.13 when on py3.7 ([80dc833](https://github.com/BradenM/micropy-cli/commit/80dc833bb6128c033e1278ba5f3a93afe70ff2e8))
* **deps:** Update dependency boltons to v23 ([27238ba](https://github.com/BradenM/micropy-cli/commit/27238ba47d817e3286b4a1b4fa0ccc1155985e6a))
* **deps:** Update dependency mypy to v1.1.1 ([a88668c](https://github.com/BradenM/micropy-cli/commit/a88668c39019ee466a388c0ca868305687537002))
* **deps:** Update dependency pydantic to v1.10.6 ([8c600f2](https://github.com/BradenM/micropy-cli/commit/8c600f24f03ae2b1349b9c483f46c21a76de3e51))
* **deps:** Update dependency python-minifier to v2.8.1 ([24ce47d](https://github.com/BradenM/micropy-cli/commit/24ce47d875e9bc5a0c4f20bfd582a313642aac78))


### Documentation

* **cfg:** Remove requirements ([2b54413](https://github.com/BradenM/micropy-cli/commit/2b54413c2e4923aeb4ed7da2e3b78d92407b3db0))
* **cfg:** Update rtd config to setup env w/ poetry. ([82c4da0](https://github.com/BradenM/micropy-cli/commit/82c4da02955080e5202bb1630a69daadce8323f4))


### Miscellaneous Chores

* **release:** Set release v4.2.0-beta ([0e2d138](https://github.com/BradenM/micropy-cli/commit/0e2d13883f7c45c36a75ee3d51e6dd63057b0b96))

## [4.1.0](https://github.com/BradenM/micropy-cli/compare/v4.1.0-beta...v4.1.0) (2023-03-05)


### Bug Fixes

* **deps:** Update dependency cachier to v2 ([956cce8](https://github.com/BradenM/micropy-cli/commit/956cce8ca8c594659fd3e57a77c65d0d65036ff9))
* **deps:** Update dependency gitpython to v3.1.31 ([65b3e83](https://github.com/BradenM/micropy-cli/commit/65b3e83fa7136765c10f268409d002f3c784c4dc))
* **deps:** Update dependency packaging to v23 ([b81b513](https://github.com/BradenM/micropy-cli/commit/b81b513248e82bb14e7d24e9b528f37fb278942b))
* **deps:** Update dependency pydantic to v1.10.5 ([0fb9624](https://github.com/BradenM/micropy-cli/commit/0fb96244da1f42207aa09cb2d1230f0c5278a6f6))
* **deps:** Update dependency tqdm to v4.65.0 ([9fb64dd](https://github.com/BradenM/micropy-cli/commit/9fb64ddb893a48b60ba41d5b25b422a7542c9b09))
* **deps:** Update dependency typing-extensions to v4.5.0 ([e6c57c3](https://github.com/BradenM/micropy-cli/commit/e6c57c30bf7f73b887437950d72593594f6f08ac))
* **pyd:** Backend rshell excess consumer kwarg, can't union with supported py versions. ([de8da4e](https://github.com/BradenM/micropy-cli/commit/de8da4e46d3033d4a1d8c25452d975a26ed1892d))


### Miscellaneous Chores

* **release:** Update release. ([7b6d9bb](https://github.com/BradenM/micropy-cli/commit/7b6d9bb90f8d003627e57e5a8d3e70bba2e104d9))

## [4.1.0-beta](https://github.com/BradenM/micropy-cli/compare/v4.0.0...v4.1.0-beta) (2023-01-30)


### Features

* **cli:** Add flag to show outdated stub packages in search + group output by repo. ([e2cdff7](https://github.com/BradenM/micropy-cli/commit/e2cdff7b0642e461182704835a6c826693c0deca))
* **cli:** Format repo as title in stubs search output. ([eaf0543](https://github.com/BradenM/micropy-cli/commit/eaf054307f4669ab1578b8bc090fbd9c1b42ab7a))
* **cli:** Improve stub search output. ([4c127ac](https://github.com/BradenM/micropy-cli/commit/4c127ac5dab299e91a56fa2d675b771b45a79cdd))
* **cli:** Utilize stub source locators during add. ([d24b409](https://github.com/BradenM/micropy-cli/commit/d24b4095c168184025be5736aee4fbc69427c6df))
* **data:** Add display names for current stub sources. ([7f6b2cd](https://github.com/BradenM/micropy-cli/commit/7f6b2cd4afe9bcea1b76029a2f94d96bc3d18de8))
* **data:** Add micropython-stubs source ([de9c2e2](https://github.com/BradenM/micropy-cli/commit/de9c2e2c987422d4871b60ab0cd7ade624c387d8))
* **deps:** Add attrs/pydantic ([06660f0](https://github.com/BradenM/micropy-cli/commit/06660f0bdaf6c06e3787dd170e7d47bd036c3722))
* **deps:** Add distlib. ([fab22ba](https://github.com/BradenM/micropy-cli/commit/fab22ba2e2fbccf791e64f825dc3545ef2c5606d))
* **deps:** Add importlib_metadata as dep. ([6acf3ca](https://github.com/BradenM/micropy-cli/commit/6acf3ca11f9dd7444aa2e39fd0a17fe52cb3226c))
* **deps:** Add pytest-clarity+better-exceptions to dev deps. ([dc9d958](https://github.com/BradenM/micropy-cli/commit/dc9d9587c5acab63fe5d6deb6b1e9e29716ee9e0))
* **main:** Drop in new StubRepository impl in place of StubRepo. ([25f0402](https://github.com/BradenM/micropy-cli/commit/25f0402fe65420337d9dffcc1a7abdc3962f2f1f))
* **main:** Init `StubRepository` as attr. ([c17be65](https://github.com/BradenM/micropy-cli/commit/c17be65e532c04bbaddd00f1d38c01abb8f1e2ff))
* **pkg:** Add __main__ module entry. ([2388858](https://github.com/BradenM/micropy-cli/commit/238885848cf47d4dadab907130a3c715341bb9d9))
* **pkg:** Cleanup package entry, dynamically resolve version. ([72ea665](https://github.com/BradenM/micropy-cli/commit/72ea66584387bc3f0db85d9042d14b9ad676884e))
* **project:** Add pylance settings to vscode template. ([bbdc936](https://github.com/BradenM/micropy-cli/commit/bbdc936c5885dfe80fde7676344ce97cada6807f))
* **project:** Assume pylance until proper refactorings can be done. ([2610c2a](https://github.com/BradenM/micropy-cli/commit/2610c2a38b83c0d525e3a31c4ff0d40fc88a7ea7))
* **stubs:** `RepoStubLocator` locate strategy. ([08f8f86](https://github.com/BradenM/micropy-cli/commit/08f8f863b2fe8b5eb75aa88374935299d03ba6c5))
* **stubs:** Accept generic package type in stub manifest ([9d17331](https://github.com/BradenM/micropy-cli/commit/9d173316f0dc7da5479523b82159d461ee990b46))
* **stubs:** Add `display_name` field to stub repository. ([a3ef03f](https://github.com/BradenM/micropy-cli/commit/a3ef03f50b79cb0139c4b0423c86f32d3c1acd30))
* **stubs:** Add `resolve_package_(absolute,)_versioned_name` to manifest. ([37bbfa6](https://github.com/BradenM/micropy-cli/commit/37bbfa678bb29cc86f1a202ec994f9e40fb6e0fa))
* **stubs:** Add method for resolving absolute stub package name from manifest. ([ad55507](https://github.com/BradenM/micropy-cli/commit/ad55507a6b19b5273ef0f9c44d1112faa7b151c9))
* **stubs:** Add MicropyStubs package/manifest models. ([021c279](https://github.com/BradenM/micropy-cli/commit/021c279fdc2b50a55a641761ecfc32bf54398b9b))
* **stubs:** Add Micropython stubs package/manifest models. ([a9297dc](https://github.com/BradenM/micropy-cli/commit/a9297dcfacbd5a31e7207b43300cd4376d9ce089))
* **stubs:** Add RepositoryInfo model. ([109aed3](https://github.com/BradenM/micropy-cli/commit/109aed30e7e588dcbeae405ead174c6b1c26d745))
* **stubs:** Add resolve package url abstract meth to stubs manifest ([8737f52](https://github.com/BradenM/micropy-cli/commit/8737f529365f556a0d3d1e4ccde1da2e2beae7aa))
* **stubs:** Add StubPackage model. ([9664111](https://github.com/BradenM/micropy-cli/commit/9664111e9304fa3149da992adbf247ec97587f1b))
* **stubs:** Add StubRepository for managing stub manifests. ([781f7cd](https://github.com/BradenM/micropy-cli/commit/781f7cddcf3912c8da89e790b717b34f04366737))
* **stubs:** Add StubRepositoryPackage model. ([e0dda9f](https://github.com/BradenM/micropy-cli/commit/e0dda9f193eca1499f3d42e0df8fb8f2e8ecc0f8))
* **stubs:** Add StubsManifest model. ([3ae9456](https://github.com/BradenM/micropy-cli/commit/3ae9456a3277e16161f0c8f29616377338afce4c))
* **stubs:** Assume latest version by default, optionally show latest only in search, general improvements in stub repo. ([b55b483](https://github.com/BradenM/micropy-cli/commit/b55b483d591990f6cdf264e2ad1fcc9f190dddb0))
* **stubs:** Build progressive package indexes in `StubRepository`, utilize in search/resolve. ([318ec13](https://github.com/BradenM/micropy-cli/commit/318ec13e1fb35e6f757ec426c1f32c712de00f9b))
* **stubs:** Check absolute name for stub resolve matching. ([142648d](https://github.com/BradenM/micropy-cli/commit/142648d4e2edabd6956723e781f6d7608a5f6030))
* **stubs:** Enforce faux immutability in StubRepository. ([a17cc5e](https://github.com/BradenM/micropy-cli/commit/a17cc5e66e938edd093ba1e4c7c283ab6d64d074))
* **stubs:** Expose `repo_name`,`versioned_name`,`absolute_versioned_name` on `StubRepositoryPackage` ([e257aa5](https://github.com/BradenM/micropy-cli/commit/e257aa56d14a98e3b81ef26c5e8fc80615036d93))
* **stubs:** Expose name/version/absolute_name fields from stub repo package. ([d88dcae](https://github.com/BradenM/micropy-cli/commit/d88dcaea98c30b41ccacd18ae7f4c1b474fa9730))
* **stubs:** Expose url via StubRepositoryPackage descriptor. ([4fd1b12](https://github.com/BradenM/micropy-cli/commit/4fd1b1202503bed22a34e861df8ff815e5a5363e))
* **stubs:** Impl `resolve_package_url` for micropython-stubs repo. ([4bd70aa](https://github.com/BradenM/micropy-cli/commit/4bd70aaea7c78ef2ae81bf432f53cef8971d5870))
* **stubs:** Impl resolve package method in StubRepository. ([c28d988](https://github.com/BradenM/micropy-cli/commit/c28d98815a31cb0c790ea4fdcaa7371f202545b0))
* **stubs:** Implement dirty metadata adapter for dist-based stubs until proper refactorings. ([a60138f](https://github.com/BradenM/micropy-cli/commit/a60138f73863b3b93e8465a38b3805aec99f88fe))
* **stubs:** Make `StubPackage` immutable. ([2fab17d](https://github.com/BradenM/micropy-cli/commit/2fab17d46445448c93bce1ca532f112507480f40))
* **stubs:** Make `StubRepository.resolve_package` return `StubRepositoryPackage` ([24ef2fa](https://github.com/BradenM/micropy-cli/commit/24ef2fa51eafcfc25b7f468288908fef736c4830))
* **stubs:** Make `StubRepositoryPackage` immutable, iterate matchers. ([ae71f91](https://github.com/BradenM/micropy-cli/commit/ae71f9136aeae81e37f03cfe7d8ea564e64f85cf))
* **stubs:** Make `StubsManifest` immutable. ([2a6ffa3](https://github.com/BradenM/micropy-cli/commit/2a6ffa3fb57e304cda88cc22c742b454a800db7a))
* **stubs:** Make `StubSource` proper abstract, add prepare abstractmethod + impls. ([d690e71](https://github.com/BradenM/micropy-cli/commit/d690e71c6c1e9d90dc4414323c6488f7fbd7d540))
* **stubs:** Make micropython stubs package sortable. ([49b6df0](https://github.com/BradenM/micropy-cli/commit/49b6df03d4b0aeb2ef124a3f2150bb3417019e80))
* **stubs:** Micropy-stubs resolve package url impl, stub micropython for now. ([c832cb0](https://github.com/BradenM/micropy-cli/commit/c832cb0c82ce3533ae18e90ada0359e375de2e1e))
* **stubs:** Rename `StubRepositoryPackage.repository` -&gt; `manifest`. ([a8b3ec8](https://github.com/BradenM/micropy-cli/commit/a8b3ec8b72094654cf665bc330e75a55bdf96b72))
* **stubs:** Support reuse of `StubSource` instances, improvements. ([b873a62](https://github.com/BradenM/micropy-cli/commit/b873a62970264928d772575c84e6c8ead305c6dc))
* **stubs:** Utilize `StubRepositoryPackage.match_exact` ([3ec08dd](https://github.com/BradenM/micropy-cli/commit/3ec08ddc5aad0bfbd8b98da6388989368d674790))
* **stubs:** Utilize locators in `StubManager`, resolve requirements from metadata. ([5c19624](https://github.com/BradenM/micropy-cli/commit/5c196249704a14d95ebfc99aa75ae77054cd8c48))
* **stubs:** Validate RepoInfo source, add method for fetching contents. ([0f7487f](https://github.com/BradenM/micropy-cli/commit/0f7487fa45beb62d149539967b6671c78cc41c60))
* **utils:** Add SupportsLessThan protocol to types util. ([489a9b0](https://github.com/BradenM/micropy-cli/commit/489a9b041082af18adf929471d7cc8f1f0bd39d5))
* **utils:** Add types to `ensure_existing_dir` ([e8e6ea8](https://github.com/BradenM/micropy-cli/commit/e8e6ea886098bd7bfceaf084e1d67673b5290177))
* **utils:** Add utils._compat module, add importlib metadata ([5722504](https://github.com/BradenM/micropy-cli/commit/572250447091e721623e562420c80295da999525))
* **utils:** Add utils.types, PathStr alias. ([63f65b9](https://github.com/BradenM/micropy-cli/commit/63f65b9b649f8de7b3d244c01a5fe17c201c4ca6))
* **utils:** Defer updating stale cache with `utils.get_cached_data` ([afd2ba5](https://github.com/BradenM/micropy-cli/commit/afd2ba5932d375a89401d1c314a3d6447a56e53f))


### Bug Fixes

* **cli:** Click fails to resolve package version. ([65ef13b](https://github.com/BradenM/micropy-cli/commit/65ef13b3939ffa65beca8eb74fd580cfd31d3382))
* **compat:** &lt;=3.8 python typing compat issues. ([e7600b4](https://github.com/BradenM/micropy-cli/commit/e7600b42a9f08035187b8644eecc66315619c753))
* **deps:** Only install import-metadata when py version &lt;3.10 ([ac1356d](https://github.com/BradenM/micropy-cli/commit/ac1356da308cfae94d16cd9935b9733ed0fad67a))
* **deps:** Pin dependencies ([84aa3c3](https://github.com/BradenM/micropy-cli/commit/84aa3c32db009121fbdd868b9664b648087186d3))
* **deps:** Pin dependencies ([1b6a46a](https://github.com/BradenM/micropy-cli/commit/1b6a46a828081d31153428e467780216737723a9))
* **deps:** Update dependency attrs to v22.2.0 ([9435223](https://github.com/BradenM/micropy-cli/commit/9435223ebe70554b6fc8d45ad68a798809ce55e2))
* **deps:** Update dependency boltons to v21 ([52bd39c](https://github.com/BradenM/micropy-cli/commit/52bd39c989dbbd1c7130ab8702d1c8caa0b50133))
* **deps:** Update dependency gitpython to v3.1.30 ([f5bb503](https://github.com/BradenM/micropy-cli/commit/f5bb5037aa1965be85198269b2ab8166f660f228))
* **deps:** Update dependency importlib-metadata to v5.2.0 ([42ab466](https://github.com/BradenM/micropy-cli/commit/42ab46681b5597eeb77d78573adcdabcd6a10bd0))
* **deps:** Update dependency markupsafe to v2.1.2 ([4239f9b](https://github.com/BradenM/micropy-cli/commit/4239f9bee88681f357b01f09b55b032ce3a5d39c))
* **deps:** Update dependency pydantic to v1.10.3 ([8d4d64d](https://github.com/BradenM/micropy-cli/commit/8d4d64ddc4ffcb64b578bd04f3f91092f6fc73a4))
* **deps:** Update dependency pydantic to v1.10.4 ([22dfef1](https://github.com/BradenM/micropy-cli/commit/22dfef18fc60db43564ee7379b319a6bb3a200e4))
* **deps:** Update dependency python-minifier to v2.8.0 ([9b0b2ef](https://github.com/BradenM/micropy-cli/commit/9b0b2efcfd2f3bc3c3ff6a8ee596329471733bd9))
* **deps:** Update dependency requests to v2.28.2 ([8e8d259](https://github.com/BradenM/micropy-cli/commit/8e8d259065b3d226aeee7be4ee0ed81cc9c7643d))
* **deps:** Update dependency requirements-parser to v0.5.0 ([26a8931](https://github.com/BradenM/micropy-cli/commit/26a8931d76121416b06fd8da90ee93e040963594))
* **main:** Add types to `MicroPy.stubs` ([2340184](https://github.com/BradenM/micropy-cli/commit/2340184db4a6e811377eab8f86f99333b8c16ab6))
* **main:** StubRepository has faux immutability. ([71feed2](https://github.com/BradenM/micropy-cli/commit/71feed28fa9a94108629784e0ba5f0de3e42ce70))
* **project:** Bad type union. ([3d32e5c](https://github.com/BradenM/micropy-cli/commit/3d32e5cb3f7802d867a7a9ac7788d33ea7c6f2cd))
* **stubs:** Ensure src path is path type in log. ([881a6a6](https://github.com/BradenM/micropy-cli/commit/881a6a6401166b621bd5eef0e76cf0b905d0b1dc))
* **stubs:** Perform repo lookups prior to adding stub ([5410a13](https://github.com/BradenM/micropy-cli/commit/5410a1369d6f693b69e36fc42b25f4a9a23c222a))
* **stubs:** Remove mutating subclass hook from `StubsManifest`. ([d3fcd7e](https://github.com/BradenM/micropy-cli/commit/d3fcd7eaef30da7e1621a507ba179a52d80ee1cf))
* **stubs:** Use `typing.Type` for sub py3.7 compat. ([1350263](https://github.com/BradenM/micropy-cli/commit/1350263bcd8b73368d99e849ead50d61b9e479b8))
* **stubs:** Utilize absolute names in stub search results. ([6c81a93](https://github.com/BradenM/micropy-cli/commit/6c81a93e8596836d13cbf5a8c0554495d5873b6b))
* **utils:** Add annotations future in type utils. ([d2d0ed8](https://github.com/BradenM/micropy-cli/commit/d2d0ed815cb46c2d7fbca6d9c1408813aa3c8565))
* **utils:** Remove PathLike GenericAlias subscript for py &lt;3.8 ([e22343a](https://github.com/BradenM/micropy-cli/commit/e22343af79972c90c68de51249f4bd48c43372b1))
* **utils:** Use importlib metadata to check micropy version in utils. ([dbeb0a9](https://github.com/BradenM/micropy-cli/commit/dbeb0a90ebe3fbe1168968198d799514382682c3))


### Documentation

* **chglog:** Remove unreleased for release-please. ([22d7be0](https://github.com/BradenM/micropy-cli/commit/22d7be04ac806653962187aa5e67f28fd07726b9))
* **conf:** Dynamically determine docs release version ([fc8ab96](https://github.com/BradenM/micropy-cli/commit/fc8ab966f8f4a44031eba42145afc244c521c896))


### Code Refactoring

* **stubs:** Remove old `StubRepo` class. ([b9de35a](https://github.com/BradenM/micropy-cli/commit/b9de35ab082b9c2e69d5b130e9e11dc45f843320))
* **stubs:** Remove search remote from stub manager. ([95d42f0](https://github.com/BradenM/micropy-cli/commit/95d42f0f643e030f4f6e3f3e6770260a445e2014))
* **stubs:** Update repository impls to retain immutability. ([b44b335](https://github.com/BradenM/micropy-cli/commit/b44b335d1a70421058fdb715fc9d95cf2bd84fae))
* **stubs:** Utilize locator strategies over stub source factory method. ([e81ac84](https://github.com/BradenM/micropy-cli/commit/e81ac8467c744f8d4462d6dd18d877c906d97773))
* **utils:** Update usage of importlib metadata. ([a09aaf9](https://github.com/BradenM/micropy-cli/commit/a09aaf9d7e15c61ad58b2a500514e58bedbb8741))

## [v4.0.0] - 2022-11-13

### Bug Fixes

-   **deps:** update dependency python-minifier to v2.7.0
-   **deps:** update dependency markupsafe to v2.1.1
-   **deps:** update dependency jinja2 to v3.1.2
-   **deps:** update dependency gitpython to v3.1.29
-   **deps:** update dependency colorama to v0.4.6
-   **deps:** pin dependencies

### Code Refactoring

-   **stubs:** utilize helper method during remote stub unpack.
-   **utils:** extract helper methods, add types.

### Features

-   **deps:** update python constraint to include v3.11, update lockfile.
-   **deps:** upgrade to click v8
-   **deps:** update all deps in-range

<a name="v4.0.0-rc.2"></a>

## [v4.0.0-rc.2] - 2022-04-17

### Bug Fixes

-   **pyd:** remove dict union operator till py3.9 min support
-   **pyd:** only type-cast rshell if type checking is enabled
-   **pyd:** capture module not found error during rshell import attempt
-   **pyd:** upydevice connect proper attr error if before established
-   **pyd:** use host path suffix check only as fallback in copy_to
-   **pyd:** consumer handler protocol methods should not be writable

### Features

-   **deps:** add upydevice+deps, missing type-stubs to dev, update mypy config
-   **deps:** upgrade upydevice and remove prev missing deps
-   **exc:** add PyDeviceError, PyDeviceConnectionError exceptions
-   **main:** update to utilize new pyd module
-   **pkg:** add pypi-test source to pyproject
-   **pkg:** regenerate changelog
-   **pkg:** add poetry+local pre-commit hook for docs export
-   **pkg:** export pyd from pkg root
-   **pkg:** add git-chlog config
-   **pyb:** add abcs for PyDevice, MetaPyDevice, Consumer/StreamConsumer
-   **pyd:** establish should return pyd instance, update consumer types
-   **pyd:** use/pass consumer handlers via delegate, expose connect/dc
-   **pyd:** add ConsumerDelegate, StreamHandlers, MessageHandlers
-   **pyd:** update rshell backend to implement MetaPyDeviceBackend
-   **pyd:** add PyDeviceConsumer protocol
-   **pyd:** add PyDevice implementation
-   **pyd:** update upyd backend to interfaces + cleanup
-   **pyd:** move tqdm-progress consumer to pyd.consumers
-   **pyd:** add Stream/Message consumer protocols+handler protos, Split MetaPyDevice/MetaPyDeviceBackend
-   **pyd:** add upydevice-based pyd backend
-   **pyd:** add rshell-based pydevice backend
-   **pyd:** rename pyb module -> pyd
-   **pyd:** add pyd module explicit exports
-   **pyd:** allow delegate_cls to be injected to pydevice via init
-   **scripts:** add script for exporting docs requirements
-   **utils:** remove pybwrapper

<a name="v4.0.0.rc.1"></a>

## [v4.0.0.rc.1] - 2022-03-14

### Bug Fixes

-   **dev-deps:** update pytest to ^7.0 to resolve py10+win pyreadline crash
-   **pkg:** rshell markers for win32
-   **pkg:** fix mistake in rshell marker
-   **pkg:** do not install rshell when py>=3.10 and on windows due to pyreadline.
-   **pkg:** win32 rshell python marker
-   **pkg:** upgrade too and pin jinja2 @ 3.0.3
-   **project:** report exception on install failure to stdout
-   **stubber:** replace pyminifer with python-minifer
-   **utils:** capture attribute err that occurs on py310 win32 rshell import
-   **utils:** utilize mp-stubbers new logic for generating stubs

### Features

-   **deps:** update dependencies scoped
-   **deps:** update micropython-stubber to latest master commit
-   **pkg:** move pytest+coverage cfg to pyproject
-   **pkg:** add missing packaging dep
-   **pkg:** update includes to be more strict
-   **pkg:** restructure and cleanup pyproject with dependency groups
-   **pkg:** merge create_stubs group into default

<a name="v3.6.0"></a>

## [v3.6.0] - 2021-05-17

### Bug Fixes

-   **data:** update stubs schema for compat with latest stubber

### Features

-   **deps:** update rshell dependency
-   **deps:** update deps, add micropy-cli w/ extras as dev-dep
-   **deps:** setup black, pre-commit
-   **pkg:** update setup file
-   **pre-commit:** add pre-commit config
-   **stubber:** update micropython-stubber submodule to latest
-   **utils:** remove dynamic
-   **utils:** refactor stub-gen to stubs, dynamically create stubber module for import

<a name="v3.5.0"></a>

## [v3.5.0] - 2020-11-17

<a name="v3.5.0.rc.1"></a>

## [v3.5.0.rc.1] - 2020-11-17

### Bug Fixes

-   full name case mismatch for pypi packages
-   package installation failures were silent
-   **pkg:** constrain questionary version to <1.8.0
-   **pkg:** setuptools editable installation issues

### Features

-   **package:** detect and return VCSDependencySource when needed in create dep source factory
-   **package:** add VCSDependencySource class for supporting VCS requirements
-   **package:** add attributes and logic for VCS packages
-   **pkg:** bump questionary dependency to ^1.8.1
-   **pkg:** add GitPython dependency

### Reverts

-   chore(deps): update setup.py

<a name="v3.4.0"></a>

## [v3.4.0] - 2020-07-25

### Bug Fixes

-   **deps:** update dpath constraint to >=1.4,<2.0

<a name="v3.3.0"></a>

## [v3.3.0] - 2019-12-23

### Bug Fixes

-   ensure any values to be extended in config are of type list ([#94](https://github.com/BradenM/micropy-cli/issues/94))
-   **utils:** ignore candidate releases when checking for update

### Features

-   **project:** generate recommended extensions with vscode integration ([#95](https://github.com/BradenM/micropy-cli/issues/95))

<a name="v3.2.0"></a>

## [v3.2.0] - 2019-12-14

<a name="v3.2.0.rc.2"></a>

## [v3.2.0.rc.2] - 2019-12-13

### Bug Fixes

-   Handle Invalid Requirements
-   **cli:** Handle errors when reading requirements from path
-   **cli:** Handle and Report Invalid Package Name Error
-   **deps:** Fix loading requirements from path
-   **utils:** Follow redirects when testing for valid url

### Code Refactoring

-   **deps:** Remove Exception handling from Packages Module

### Features

-   Add Base and Requirement Exceptions
-   **poetry:** Update Poetry to Stable

<a name="v3.2.0.rc.1"></a>

## [v3.2.0.rc.1] - 2019-12-09

### Bug Fixes

-   Make rshell and pyminifier requirements optional ([#82](https://github.com/BradenM/micropy-cli/issues/82))
-   Colorama Version Constraint
-   Colorama Broken Release, Style
-   VSCode Settings failed to populate on reload ([#81](https://github.com/BradenM/micropy-cli/issues/81))
-   **config:** Remove concrete path from ConfigSource
-   **config:** Remove cache method for better implementation later
-   **deps:** Temporary Directory would be removed before it was ready
-   **logger:** Exception formatting
-   **project:** Context not being updated when needed
-   **project:** Add empty dict to config on create

### Code Refactoring

-   Cleanup Stubs Module Context Handling
-   **packages:** Use new Dependency Api in Packages Module

### Features

-   **cli:** Basic install from path option implementation
-   **config:** Manage sync via callback
-   **config:** New Interface with file/memory autosync and dot notation
-   **config:** Dictionary Config Source
-   **config:** Cache and Root Key Context Manager for Config Items
-   **config:** Use dpath to handle Config paths and merging
-   **config:** Improved handling of collection data types
-   **config:** Add pop method to config
-   **config:** New and Improved Config File Interface
-   **context:** Use DictConfig for Project Context
-   **deps:** Package Class for representing a requirement
-   **deps:** Address Package Source Uniformly
-   **deps:** Allow local deps to be sourced from anywhere
-   **project:** Add local-lib-path config option.
-   **project:** Load Project Modules by individual Priority
-   **project:** Update Projects to use Priority Queue
-   **project:** Implement Dependencies in Project Module
-   **project:** Render Local Deps in Project Settings
-   **project:** Update Config/Context automatically
-   **project:** Update modules to use new, more flexible config
-   **project:** Use new Config Interface in Projects
-   **project:** Try to add local deps as relative to project, fallback...
-   **project:** Replace Project Cache with Config Instance
-   **template:** Update TemplateModule

### Performance Improvements

-   **size:** Slimmed Package Size

<a name="v3.1.1"></a>

## [v3.1.1] - 2019-12-03

### Bug Fixes

-   HookProxy failed to resolve with kwargs
-   **checks:** VSCode check failing on py36
-   **logger:** Exception formatting
-   **package:** Add metadata to pyproject.toml
-   **package:** Update Makefile and bump2version to use pyproject
-   **package:** Use Dephell to generate setup.py, Remove Manifiest.in
-   **project:** Exception Raised if no Templates are used in Project
-   **project:** VSCode check always failed silently

### Features

-   Cleanup Log File Formatting
-   Use Poetry for Dependency Management

### Performance Improvements

-   **size:** Slimmed Package Size

<a name="v3.1.0"></a>

## [v3.1.0] - 2019-11-12

### Bug Fixes

-   Handle Errors when adding Packages
-   Project Context Stub Path Ordering
-   HookProxy failed to work with descriptors.
-   PackagesModule Dev, Project Context
-   Move Template Check flag to TemplatesModule
-   Active Project Resolve, Cli Templates List

### Code Refactoring

-   Add Packages from File
-   Import MicroPy and Modules to Package Root
-   Restructure Project Module

### Features

-   Report Ready on Project Load, Code Cleanup
-   Write .gitignore file in generated .micropy folder
-   Proxy Project Hooks to allow hooks with the same name, Split De...
-   Resolve Project Hooks via attrs, Fix Stub List
-   **project:** Project Method Hook Decorator

### Performance Improvements

-   Lazy Load Project Stubs

<a name="v3.0.1"></a>

## [v3.0.1] - 2019-10-13

### Bug Fixes

-   Auto Update Check's Cache not expiring after update
-   VSCode Template Check always Fails on Linux ([#65](https://github.com/BradenM/micropy-cli/issues/65))
-   **upstream:** Fails to Generate Stub Files

<a name="v3.0.0"></a>

## [v3.0.0] - 2019-10-13

### Bug Fixes

-   Project Fails to Init due to Checks on Windows
-   Stub Package Url fails to resolve on Windows
-   Handle Chunked Content Length on Package Download
-   Package urls not resolving correctly
-   Fails to load Project if Template Files are Missing ([#55](https://github.com/BradenM/micropy-cli/issues/55))

### Code Refactoring

-   **data:** Move all Data Paths to Data Module

### Features

-   Add Flag for Skipping Template Checks
-   Search/Retrieve Stubs Directly from micropy-stubs
-   Update MicropyCli Stub Sources
-   Refactor MicroPy Class for Better State Management

### Performance Improvements

-   Lazy Load Stubs when Needed
-   **project:** Lazy Load Current Active Project

### BREAKING CHANGE

micropy.STUBS renamed to micropy.stubs

<a name="v2.2.0"></a>

## [v2.2.0] - 2019-09-28

### Features

-   Template Checks, MS-Python Check ([#52](https://github.com/BradenM/micropy-cli/issues/52))
-   **cli:** Automatic Update Checks ([#54](https://github.com/BradenM/micropy-cli/issues/54))
-   **vscode:** Ensure Jedi is Disabled in VSCode Template

### Performance Improvements

-   **stubs:** Cache Available Stubs for Searching

<a name="v2.1.1"></a>

## [v2.1.1] - 2019-09-22

### Bug Fixes

-   **hotfix:** Remove workspaceRoot var from VSCode Settings ([#51](https://github.com/BradenM/micropy-cli/issues/51))

### Features

-   Relicensed under MIT

### BREAKING CHANGE

No longer compatible with <=ms-python.python[@2019](https://github.com/2019).8.30787 VSCode Extension

<a name="v2.1.0"></a>

## [v2.1.0] - 2019-09-01

### Bug Fixes

-   **project:** Requirement Files skipped on First Init
-   **windows:** Support User Level Directory Linking ([#45](https://github.com/BradenM/micropy-cli/issues/45))

### Features

-   **log:** Cap Log File at 2MB
-   **project:** Init Project with Micropy Dev Dependency
-   **project:** Git Ignore Template Option

<a name="v2.0.2"></a>

## [v2.0.2] - 2019-08-21

### Bug Fixes

-   **dep:** Require appropriate Click version
-   **windows:** Warn User if MicroPy Lacks Admin Privs

<a name="v2.0.1"></a>

## [v2.0.1] - 2019-07-26

### Bug Fixes

-   **stubs:** Reduce Schema Strictness

<a name="v2.0.0"></a>

## [v2.0.0] - 2019-07-25

### Bug Fixes

-   **dep:** Broken Docutils Dependency
-   **project:** Only modules install correctly

### Features

-   Add Optional Pyminifier Dep for Stub Creation
-   **cli:** Install Python Packages for Project
-   **cli:** Verbosity Flag for Stub Creation
-   **dep:** Update Tox to latest
-   **dep:** Packaging Module Requirement
-   **lib:** Update Stubber to Process Branch
-   **project:** Update requirements.txt Files on Install
-   **project:** Template Update Functionality
-   **project:** Install from Requirements.txt
-   **project:** Retrieve and Stub Project Requirements
-   **project:** Project Config in Info File
-   **project:** Make Templates Optional via CLI ([#30](https://github.com/BradenM/micropy-cli/issues/30))
-   **pyb:** Handle Pyboard Output and Errors
-   **stubs:** Minify Stubber Before Executing
-   **util:** Generate Stub from File Utility

<a name="v1.1.3"></a>

## [v1.1.3] - 2019-07-20

### Bug Fixes

-   ValueError raised after Creating Project in Windows ([#33](https://github.com/BradenM/micropy-cli/issues/33))
-   Unicode Error raised when logging on Windows ([#32](https://github.com/BradenM/micropy-cli/issues/32))

<a name="v1.1.2"></a>

## [v1.1.2] - 2019-07-19

### Bug Fixes

-   **stubs:** Ensure Firmware Stubs Load First

<a name="v1.1.1"></a>

## [v1.1.1] - 2019-07-17

### Bug Fixes

-   Temp Hotfix for False Stub Duplication

<a name="v1.1.0"></a>

## [v1.1.0] - 2019-07-16

### Bug Fixes

-   **cli:** Stub List always prints Unknown
-   **cli:** Made Stub Search Case Insensitive
-   **stubs:** FileExistsError when adding existing Stub

### Features

-   **cli:** List Project Stubs if in Project Directory
-   **cli:** Stubs now list by Firmware
-   **cli:** Create Formatted Strings from Logger
-   **cli:** Added --force flag when adding stubs
-   **project:** Micropy Project Info File ([#29](https://github.com/BradenM/micropy-cli/issues/29))
-   **project:** Micropy Project Folder ([#28](https://github.com/BradenM/micropy-cli/issues/28))

<a name="v1.0.0"></a>

## [v1.0.0] - 2019-07-11

### Bug Fixes

-   **cli:** Init Crashes if no Stubs are Loaded
-   **cli:** Create Stubs Help Formatting
-   **log:** Output Highlight Bug, Cleanup
-   **stub:** Stub Name without Firmware
-   **stubs:** Firmware not showing as Installed in Stub Search
-   **stubs:** Fix Existing Firmware Reinstall

### Features

-   Implemented Local and Remote Stub Sources ([#18](https://github.com/BradenM/micropy-cli/issues/18))
-   **cli:** Minified Cli Output Style
-   **cli:** Search Available Stubs ([#27](https://github.com/BradenM/micropy-cli/issues/27))
-   **cli:** Stream Downloads with Progress Bar
-   **stub:** Update Stubs to Use New Stubber Schema ([#23](https://github.com/BradenM/micropy-cli/issues/23))
-   **stubs:** Updated micropython-stubber to latest
-   **stubs:** Add Firmware Frozen Modules to Templates
-   **stubs:** Device Stubs Firmware Resolution ([#25](https://github.com/BradenM/micropy-cli/issues/25))
-   **stubs:** Add Device Frozen Modules to Templates ([#24](https://github.com/BradenM/micropy-cli/issues/24))
-   **stubs:** Added Stub Stdout Verbosity
-   **stubs:** Add Stubs from Repositories ([#21](https://github.com/BradenM/micropy-cli/issues/21))
-   **stubs:** Replaced Stubs with Stub "Packages"
-   **stubs:** Stub Repositories ([#20](https://github.com/BradenM/micropy-cli/issues/20))
-   **stubs:** Update Stub Creation ([#26](https://github.com/BradenM/micropy-cli/issues/26))
-   **util:** Generic Utility Functions and Module Cleanup

### Performance Improvements

-   **cli:** Only Instantiate MicroPy when needed

<a name="v0.3.0"></a>

## [v0.3.0] - 2019-06-25

### Code Refactoring

-   MicroPy to use new Stub and Utility Features ([#14](https://github.com/BradenM/micropy-cli/issues/14))

### Features

-   **cli:** Version Flag
-   **log:** New Cli Output Style, Log Class Methods
-   **pyb:** PyboardWrapper Utility ([#13](https://github.com/BradenM/micropy-cli/issues/13))
-   **stubs:** Stub Manager ([#5](https://github.com/BradenM/micropy-cli/issues/5))
-   **utils:** Utils Module and Validator Utility ([#4](https://github.com/BradenM/micropy-cli/issues/4))

<a name="v0.2.0"></a>

## [v0.2.0] - 2019-06-14

### Features

-   **log:** Added Proper Log Formatting, cleaned messages before write.
-   **log:** Added Logging to Template Module
-   **project:** Drop Cookiecutter for Purely Jinja2 ([#3](https://github.com/BradenM/micropy-cli/issues/3))

<a name="v0.1.1"></a>

## [v0.1.1] - 2019-06-10

### Bug Fixes

-   **setup:** Fixed missing cookiecutter package requirement
-   **setup:** Fixed Pypi misinformation, cleaned up dist-management files
-   **setup:** Fix Missing .vscode Template Files

<a name="v0.1.0"></a>

## v0.1.0 - 2019-06-09

### Bug Fixes

-   Fails First Time Setup Failed to init on first run if the stubs folder didn't exist
-   Removed old command
-   Fix Project Init
-   Added rshell to setup.py
-   Quick Fix before Project Class Restructure
-   Packaging Fixes
-   **package:** Allow multiple versions of python, Update Reqs
-   **setup:** Included Template in Manifest
-   **stub:** Fixed Refresh Stubs
-   **stubs:** Cleaned Stub Names before Adding
-   **stubs:** Removed Old Stub Command
-   **stubs:** Fixed missing logging.py
-   **template:** Fixed src template

### Code Refactoring

-   Setup as proper package

### Features

-   Project Init and Template Serialization
-   Finished Package Setup and Structure
-   Let Stub class handle validation and files
-   Setup Template Files
-   Initial commit
-   Add Josverl Stubs on First Setup, Restructured MicroPy
-   Added MicroPy Parent Class
-   Added stubber as submodule over pulling files with requests
-   **log:** Added Silet Stdout Context Manager to Logger
-   **log:** Setup ServiceLog to work as a single parent Logger with ch...
-   **log:** Added Logging
-   **log:** Setup Logger as Borg for easy access
-   **log:** Added file logging to ServiceLog, Added docs
-   **project:** Project Module Rewrite to use Cookiecutter and JSON
-   **pylint:** Added checkbox to choose stubs for pylint
-   **stub:** Pass Multiple Stubs to .pylintrc
-   **stub:** Added stub add, refresh commands
-   **stub:** Added createstub.py download
-   **stub:** Added Stub Class, Moved Stub logic to MicroPy/Stub
-   **stubs:** Added Automated Stub Creation on PyBoard
-   **stubs:** Added Stub Validation, Stub Class Restructure
-   **stubs:** Added Basic Stub Exceptions
-   **template:** Setup Template in Cookiecutter Fashion

[v4.0.0]: https://github.com/BradenM/micropy-cli/compare/v4.0.0-rc.2...v4.0.0
[v4.0.0-rc.2]: https://github.com/BradenM/micropy-cli/compare/v4.0.0.rc.1...v4.0.0-rc.2
[v4.0.0.rc.1]: https://github.com/BradenM/micropy-cli/compare/v3.6.0...v4.0.0.rc.1
[v3.6.0]: https://github.com/BradenM/micropy-cli/compare/v3.5.0...v3.6.0
[v3.5.0]: https://github.com/BradenM/micropy-cli/compare/v3.5.0.rc.1...v3.5.0
[v3.5.0.rc.1]: https://github.com/BradenM/micropy-cli/compare/v3.4.0...v3.5.0.rc.1
[v3.4.0]: https://github.com/BradenM/micropy-cli/compare/v3.3.0...v3.4.0
[v3.3.0]: https://github.com/BradenM/micropy-cli/compare/v3.2.0...v3.3.0
[v3.2.0]: https://github.com/BradenM/micropy-cli/compare/v3.2.0.rc.2...v3.2.0
[v3.2.0.rc.2]: https://github.com/BradenM/micropy-cli/compare/v3.2.0.rc.1...v3.2.0.rc.2
[v3.2.0.rc.1]: https://github.com/BradenM/micropy-cli/compare/v3.1.1...v3.2.0.rc.1
[v3.1.1]: https://github.com/BradenM/micropy-cli/compare/v3.1.0...v3.1.1
[v3.1.0]: https://github.com/BradenM/micropy-cli/compare/v3.0.1...v3.1.0
[v3.0.1]: https://github.com/BradenM/micropy-cli/compare/v3.0.0...v3.0.1
[v3.0.0]: https://github.com/BradenM/micropy-cli/compare/v2.2.0...v3.0.0
[v2.2.0]: https://github.com/BradenM/micropy-cli/compare/v2.1.1...v2.2.0
[v2.1.1]: https://github.com/BradenM/micropy-cli/compare/v2.1.0...v2.1.1
[v2.1.0]: https://github.com/BradenM/micropy-cli/compare/v2.0.2...v2.1.0
[v2.0.2]: https://github.com/BradenM/micropy-cli/compare/v2.0.1...v2.0.2
[v2.0.1]: https://github.com/BradenM/micropy-cli/compare/v2.0.0...v2.0.1
[v2.0.0]: https://github.com/BradenM/micropy-cli/compare/v1.1.3...v2.0.0
[v1.1.3]: https://github.com/BradenM/micropy-cli/compare/v1.1.2...v1.1.3
[v1.1.2]: https://github.com/BradenM/micropy-cli/compare/v1.1.1...v1.1.2
[v1.1.1]: https://github.com/BradenM/micropy-cli/compare/v1.1.0...v1.1.1
[v1.1.0]: https://github.com/BradenM/micropy-cli/compare/v1.0.0...v1.1.0
[v1.0.0]: https://github.com/BradenM/micropy-cli/compare/v0.3.0...v1.0.0
[v0.3.0]: https://github.com/BradenM/micropy-cli/compare/v0.2.0...v0.3.0
[v0.2.0]: https://github.com/BradenM/micropy-cli/compare/v0.1.1...v0.2.0
[v0.1.1]: https://github.com/BradenM/micropy-cli/compare/v0.1.0...v0.1.1


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2019 Braden Mars

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: Makefile
================================================
.PHONY: clean clean-test clean-pyc clean-build
bold := $(shell tput bold)
rsttxt := $(shell tput sgr0)


clean: clean-build clean-pyc clean-test ## remove all build, test, coverage and Python artifacts

clean-build: ## remove build artifacts
	@printf '$(bold)Cleaning Artifacts...\n$(rsttxt)'
	rm -fr build/
	rm -fr dist/
	rm -fr .eggs/
	rm -fr pip-wheel-metadata/
	find . -name '*.egg-info' -exec rm -fr {} +
	find . -name '*.egg' -exec rm -f {} +

clean-pyc: ## remove Python file artifacts
	find . -name '*.pyc' -exec rm -f {} +
	find . -name '*.pyo' -exec rm -f {} +
	find . -name '*~' -exec rm -f {} +
	find . -name '__pycache__' -exec rm -fr {} +

clean-test: ## remove test and coverage artifacts
	- rm -fr .tox/
	- rm -f .coverage
	- rm -fr htmlcov/
	find . -name '.pytest_cache' -exec rm -fr {} +
	- rm -f .testmondata
	- rm -rf .tmontmp
	- rm cov.xml test_log.xml

test: ## run tests quickly with the default Python
	pytest

watch-build: clean ## build pytest-testmon db
	pytest --testmon -c pyproject.toml

watch: clean ## watch tests
	- pytest --testmon
	ptw --spool 2000 --onpass "make watch-cov" --clear -- --testmon -vv -c pyproject.toml

watch-cov: ## watch test coverage
	pytest -n'auto' --forked --cov --cov-append --cov-config=pyproject.toml --cov-report=xml:cov.xml --cov-report term

coverage: ## generate coverage
	pytest -n'auto' --cov --cov-config=pyproject.toml

coverage-html: ## generate coverage html
	pytest -n'auto' --cov --cov-config=pyproject.toml --cov-report html

gendoc: ## Generate Docs
	$(MAKE) -C docs clean
	$(MAKE) -C docs html
	@printf '$(bold)Docs Generated!\n$(rsttxt)'

test-release: dist ## release on pypi-test repo
	@printf '$(bold)Uploading Test Release to TestPyPi...\n$(rsttxt)'
	poetry publish -r test
	@printf '$(bold)Test Released published!\n$(rsttxt)'

release: dist ## package and release
	@printf '$(bold)Uploading package to PyPi...\n$(rsttxt)'
	poetry publish
	git push --tags
	@printf '$(bold)Done! Tags Pushed!\n$(rsttxt)'

dist: clean ## builds package
	@printf '$(bold)Building Source and Wheel...\n$(rsttxt)'
	- rm README.rst
	poetry build
	ls -l dist

install: clean ## install pkg
	python setup.py install


================================================
FILE: README.md
================================================
# Micropy Cli [![PyPI][pypi-img]][pypi-url] [![PyPI - Python Version][pypiv-img]][pypi-url] [![Github - Test Micropy Cli][build-img]][build-url] [![Coverage Status][cover-img]][cover-url]


Micropy Cli is a project management/generation tool for writing [Micropython](https://micropython.org/) code in modern IDEs such as VSCode.
Its primary goal is to automate the process of creating a workspace complete with:

* **Linting** compatible with Micropython
* VSCode **Intellisense**
* **Autocompletion**
* Dependency Management
* VCS Compatibility


<p align='center'>
    <img width='95%' src='.github/img/micropy.svg' alt="Micropy Demo SVG">
</p>

[pypi-img]: https://img.shields.io/pypi/v/micropy-cli?logo=pypi&logoColor=white&style=flat-square
[pypi-url]: https://pypi.org/project/micropy-cli/
[pypiv-img]: https://img.shields.io/pypi/pyversions/micropy-cli.svg?style=flat-square&logo=python&logoColor=green
[build-img]: https://img.shields.io/github/workflow/status/BradenM/micropy-cli/Test%20MicropyCli/master?logo=github&style=flat-square
[build-url]: https://github.com/BradenM/micropy-cli/actions
[cover-img]: https://img.shields.io/coveralls/github/BradenM/micropy-cli/master?style=flat-square&logo=coveralls
[cover-url]: https://coveralls.io/github/BradenM/micropy-cli

# Getting Started

## Installation

You can download and install the latest version of this software from the Python package index (PyPI) as follows:

`pip install --upgrade micropy-cli`

If applicable, you can test out a pre-release by executing:

`pip install --upgrade --pre micropy-cli`



## Creating a Project

Creating a new project folder is as simple as:

1. Executing `micropy init <PROJECT NAME>`
2. Selecting which features to enable
3. Selecting your target device/firmware
4. Boom. Your workspace is ready.

<p align='center'>
    <img src='https://github.com/BradenM/micropy-cli/raw/master/.github/img/demo.gif' alt="Micropy Demo">
</p>


## Micropy Project Environment

When creating a project with `micropy-cli`, two special items are added:

* A `.micropy/` folder
* A `micropy.json` file

The `.micropy/` contains symlinks from your project to your `$HOME/.micropy/stubs` folder. By doing this, micropy can reference the required stub files for your project as relative to it, rather than using absolute paths to `$HOME/.micropy`. How does this benefit you? Thanks to this feature, you can feel free to push common setting files such as `settings.json` and `.pylint.rc` to your remote git repository. This way, others who clone your repo can achieve a matching workspace in their local environment.

> Note: The generated `.micropy/` folder should be *IGNORED* by your VCS. It is created locally for each environment via the `micropy.json` file.

The `micropy.json` file contains information micropy needs in order to resolve your projects required files when other clone your repo. Think of it as a `package.json` for micropython.

## Cloning a Micropy Environment

To setup a Micropy environment locally, simply:

* Install `micropy-cli`
* Navigate to the project directory
* Execute `micropy`

Micropy will automatically configure and install any stubs required by a project thanks to its `micropy.json` file.

## Project Dependencies

While all modules that are included in your targeted micropython firmware are available with autocompletion, intellisense, and linting, most projects require external dependencies.

Currently, handling dependencies with micropython is a bit tricky. Maybe you can install a cpython version of your requirement? Maybe you could just copy and paste it? What if it needs to be frozen?

Micropy handles all these issues for you automatically. Not only does it track your project's dependencies, it keeps both `requirements.txt` and `dev-requirements.txt` updated, enables autocompletion/intellisense for each dep, and allows you to import them just as you would on your device.

This allows you to include your requirement however you want, whether that be as a frozen module in your custom built firmware, or simply in the `/lib` folder on your device.

#### Installing Packages

To add a package as a requirement for your project, run:

`micropy install <PACKAGE_NAMES>`

while in your project's root directory.

This will automatically execute the following:

* Source `PACKAGE_NAMES` from pypi, as a url, or a local path
* Retrieve the module/package and stub it, adding it to your local `.micropy` folder.
* Add requirement to your `micropy.json`
* Update `requirements.txt`

To install dev packages that are not needed on your device, but are needed for local development, add the `--dev` flag. This will do everything above **except** stub the requirement.

You can also install all requirements found in `micropy.json`/`requirements.txt`/`dev-requirements.txt` by executing `micropy install` without passing any packages. Micropy will automatically do this when setting up a local environment of an existing micropy project.

#### Example

Lets say your new project will depend on [picoweb](https://pypi.org/project/picoweb/) and [blynklib](https://pypi.org/project/blynklib/). Plus, you'd like to use [rshell](https://pypi.org/project/rshell/) to communicate directly with your device. After creating your project via `micropy init`, you can install your requirements as so:

<p align='center'>
    <img width="70%" src='.github/img/install_demo.svg' alt="Micropy Pkg Install Demo">
</p>

Now you or anybody cloning your project can import those requirements normally, and have the benefits of all the features micropy brings:

<p align='center'>
    <img width="70%" src='https://github.com/BradenM/micropy-cli/raw/master/.github/img/deps_demo.gif' alt="Micropy Deps Demo">
</p>


## Stub Management

Stub files are the magic behind how micropy allows features such as linting, Intellisense, and autocompletion to work. To achieve the best results with MicropyCli, its important that you first add the appropriate stubs for the device/firmware your project uses.

> Note: When working in a micropy project, all stub related commands will also be executed on the active project. (i.e if in a project and you run `micropy stubs add <stub-name>`, then that stub retrieved AND added to the active project.)

### Adding Stubs

Adding stubs to Micropy is a breeze. Simply run: `micropy stubs add <STUB_NAME>`
By sourcing [micropy-stubs](https://github.com/BradenM/micropy-stubs), MicroPy has several premade stub packages to choose from.

These packages generally use the following naming schema:

`<device>-<firmware>-<version>`

For example, running `micropy stubs add esp32-micropython-1.11.0` will install the following:
* Micropython Specific Stubs
* ESP32 Micropython v1.11 Device Specific Stubs
* Frozen Modules for both device and firmware

You can search stubs that are made available to Micropy via `micropy stubs search <QUERY>`

Alternatively, using `micropy stubs add <PATH>`, you can manually add stubs to Micropy.
For manual stub generation, please see [Josvel/micropython-stubber](https://github.com/Josverl/micropython-stubber).

### Creating Stubs

Using `micropy stubs create <PORT/IP_ADDRESS>`, MicropyCli can automatically generate and add stubs from any Micropython device you have on hand. This can be done over both USB and WiFi.

> Note: For stub creation, micropy-cli has additional dependencies.
>
> These can be installed by executing: `pip install micropy-cli[create_stubs]`


### Viewing Stubs

To list stubs you have installed, simply run `micropy stubs list`.

To search for stubs for your device, use `micropy stubs search <QUERY>`.

# See Also

* [VSCode IntelliSense, Autocompletion & Linting capabilities][lemariva-blog]
    - An awesome article written by [lemariva](https://github.com/lemariva). It covers creating a micropython project environment from scratch using `micropy-cli` and [pymakr-vsc](pymakr-vsc). Great place to start if you're new to this!

* [Developing for the Raspberry Pi Pico in VS Code][cpwood-medium]
    - A getting started guide for developing in micropython on the Raspberry Pi Pico by [cpwood][cpwood-git].
    - Also see: [Pico-Go: Micropy-Cli][cpwood-picogo]

* [Awesome MicroPython][awesome-micropy]
    - Collection of awesome micropython libraries / resources.
    - Features `micropy-cli` along with several other great development tools under the [Development][awesome-micropy-develop] category.


[lemariva-blog]: https://lemariva.com/blog/2019/08/micropython-vsc-ide-intellisense
[lemariva-git]:  https://github.com/lemariva

[cpwood-medium]: https://medium.com/all-geek-to-me/developing-for-the-raspberry-pi-pico-in-vs-code-getting-started-6dbb3da5ba97
[cpwood-picogo]: http://pico-go.net/docs/help/micropy/
[cpwood-git]: https://github.com/cpwood/

[awesome-micropy]: https://awesome-micropython.com/
[awesome-micropy-develop]: https://awesome-micropython.com/#development

# Acknowledgements

## Micropython-Stubber
[Josvel/micropython-stubber](https://github.com/Josverl/micropython-stubber)

Josverl's Repo is full of information regarding Micropython compatibility with VSCode and more. To find out more about how this process works, take a look at it.

micropy-cli and [micropy-stubs](https://github.com/BradenM/micropy-stubs) depend on micropython-stubber for its ability to generate frozen modules, create stubs on a pyboard, and more.


================================================
FILE: docs/Makefile
================================================
# Minimal makefile for Sphinx documentation
#

# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS    ?=
SPHINXBUILD   ?= sphinx-build
SOURCEDIR     = .
BUILDDIR      = _build

# Put it first so that "make" without argument is like "make help".
help:
	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

.PHONY: help Makefile

# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)


================================================
FILE: docs/_autosummary/micropy.config.config_source.rst
================================================
micropy.config.config\_source
=============================

.. automodule:: micropy.config.config_source







   .. rubric:: Classes

   .. autosummary::

      ConfigSource


================================================
FILE: docs/_autosummary/micropy.config.rst
================================================
micropy.config
==============

.. automodule:: micropy.config







   .. rubric:: Classes

   .. autosummary::

      Config
      JSONConfigSource
      DictConfigSource


================================================
FILE: docs/_autosummary/micropy.exceptions.rst
================================================
micropy.exceptions
==================

.. automodule:: micropy.exceptions

   
   
   

   
   
   

   
   
   .. rubric:: Exceptions

   .. autosummary::
   
      StubError
      StubNotFound
      StubValidationError
   
   


================================================
FILE: docs/_autosummary/micropy.main.rst
================================================
micropy.main
============

.. automodule:: micropy.main







   .. rubric:: Classes

   .. autosummary::

      MicroPy


================================================
FILE: docs/_autosummary/micropy.packages.rst
================================================
micropy.packages
================

.. automodule:: micropy.packages



   .. rubric:: Functions

   .. autosummary::

      create_dependency_source





   .. rubric:: Classes

   .. autosummary::

      LocalDependencySource
      Package
      PackageDependencySource


================================================
FILE: docs/_autosummary/micropy.project.modules.rst
================================================
micropy.project.modules
=======================

.. automodule:: micropy.project.modules







   .. rubric:: Classes

   .. autosummary::

      DevPackagesModule
      PackagesModule
      ProjectModule
      StubsModule
      TemplatesModule
      HookProxy


================================================
FILE: docs/_autosummary/micropy.project.rst
================================================
micropy.project
===============

.. automodule:: micropy.project

   
   
   

   
   
   .. rubric:: Classes

   .. autosummary::
   
      Project
   
   

   
   
   


================================================
FILE: docs/_autosummary/micropy.rst
================================================
micropy
=======

.. automodule:: micropy

   
   
   

   
   
   

   
   
   


================================================
FILE: docs/_autosummary/micropy.stubs.rst
================================================
micropy.stubs
=============

.. automodule:: micropy.stubs







   .. rubric:: Classes

   .. autosummary::

      StubManager
      source


================================================
FILE: docs/_autosummary/micropy.stubs.source.rst
================================================
micropy.stubs.source
====================

.. automodule:: micropy.stubs.source



   .. rubric:: Functions

   .. autosummary::

      get_source





   .. rubric:: Classes

   .. autosummary::

      LocalStubSource
      RemoteStubSource
      StubRepo
      StubSource


================================================
FILE: docs/_autosummary/micropy.utils.rst
================================================
micropy.utils
=============

.. automodule:: micropy.utils



   .. rubric:: Functions

   .. autosummary::

      create_dir_link
      ensure_existing_dir
      ensure_valid_url
      extract_tarbytes
      generate_stub
      get_package_meta
      get_url_filename
      is_dir_link
      is_downloadable
      is_existing_dir
      is_url
      iter_requirements
      search_xml
      stream_download
      is_update_available
      get_cached_data
      get_class_that_defined_method





   .. rubric:: Classes

   .. autosummary::

      PyboardWrapper
      Validator


================================================
FILE: docs/base.md
================================================
## Installation

You can download and install the latest version of this software from the Python package index (PyPI) as follows:

`pip install --upgrade micropy-cli`

If applicable, you can test out a pre-release by executing:

`pip install --upgrade --pre micropy-cli`


# Getting Started

## Creating a Project

Creating a new project folder is as simple as:

1. Executing `micropy init <PROJECT NAME>`
2. Selecting which features to enable
3. Selecting your target device/firmware
4. Boom. Your workspace is ready.

<p align='center'>
    <img src='https://github.com/BradenM/micropy-cli/raw/master/.github/img/demo.gif' alt="Micropy Demo">
</p>


## Micropy Project Environment

When creating a project with `micropy-cli`, two special items are added:

* A `.micropy/` folder
* A `micropy.json` file

The `.micropy/` contains symlinks from your project to your `$HOME/.micropy/stubs` folder. By doing this, micropy can reference the required stub files for your project as relative to it, rather than using absolute paths to `$HOME/.micropy`. How does this benefit you? Thanks to this feature, you can feel free to push common setting files such as `settings.json` and `.pylint.rc` to your remote git repository. This way, others who clone your repo can achieve a matching workspace in their local environment.

> Note: The generated `.micropy/` folder should be *IGNORED* by your VCS. It is created locally for each environment via the `micropy.json` file.

The `micropy.json` file contains information micropy needs in order to resolve your projects required files when other clone your repo. Think of it as a `package.json` for micropython.

## Cloning a Micropy Environment

To setup a Micropy environment locally, simply:

* Install `micropy-cli`
* Navigate to the project directory
* Execute `micropy`

Micropy will automatically configure and install any stubs required by a project thanks to its `micropy.json` file.

## Project Dependencies

While all modules that are included in your targeted micropython firmware are available with autocompletion, intellisense, and linting, most projects require external dependencies.

Currently, handling dependencies with micropython is a bit tricky. Maybe you can install a cpython version of your requirement? Maybe you could just copy and paste it? What if it needs to be frozen?

Micropy handles all these issues for you automatically. Not only does it track your project's dependencies, it keeps both `requirements.txt` and `dev-requirements.txt` updated, enables autocompletion/intellisense for each dep, and allows you to import them just as you would on your device.

This allows you to include your requirement however you want, whether that be as a frozen module in your custom built firmware, or simply in the `/lib` folder on your device.

#### Installing Packages

To add a package as a requirement for your project, run:

`micropy install <PACKAGE_NAMES>`

while in your project's root directory.

This will automatically execute the following:

* Source `PACKAGE_NAMES` from pypi, as a url, or a local path
* Retrieve the module/package and stub it, adding it to your local `.micropy` folder.
* Add requirement to your `micropy.json`
* Update `requirements.txt`

To install dev packages that are not needed on your device, but are needed for local development, add the `--dev` flag. This will do everything above **except** stub the requirement.

You can also install all requirements found in `micropy.json`/`requirements.txt`/`dev-requirements.txt` by executing `micropy install` without passing any packages. Micropy will automatically do this when setting up a local environment of an existing micropy project.

#### Example

Lets say your new project will depend on [picoweb](https://pypi.org/project/picoweb/) and [blynklib](https://pypi.org/project/blynklib/). Plus, you'd like to use [rshell](https://pypi.org/project/rshell/) to communicate directly with your device. After creating your project via `micropy init`, you can install your requirements as so:

<p align='center'>
    <img width="70%" src='https://github.com/BradenM/micropy-cli/raw/master/.github/img/install_demo.svg' alt="Micropy Pkg Install Demo">
</p>

Now you or anybody cloning your project can import those requirements normally, and have the benefits of all the features micropy brings:

<p align='center'>
    <img width="70%" src='https://github.com/BradenM/micropy-cli/raw/master/.github/img/deps_demo.gif' alt="Micropy Deps Demo">
</p>


## Stub Management

Stub files are the magic behind how micropy allows features such as linting, Intellisense, and autocompletion to work. To achieve the best results with MicropyCli, its important that you first add the appropriate stubs for the device/firmware your project uses.

> Note: When working in a micropy project, all stub related commands will also be executed on the active project. (i.e if in a project and you run `micropy stubs add <stub-name>`, then that stub retrieved AND added to the active project.)

### Adding Stubs

Adding stubs to Micropy is a breeze. Simply run: `micropy stubs add <STUB_NAME>`
By sourcing [micropy-stubs](https://github.com/BradenM/micropy-stubs), MicroPy has several premade stub packages to choose from.

These packages generally use the following naming schema:

`<device>-<firmware>-<version>`

For example, running `micropy stubs add esp32-micropython-1.11.0` will install the following:
* Micropython Specific Stubs
* ESP32 Micropython v1.11 Device Specific Stubs
* Frozen Modules for both device and firmware

You can search stubs that are made available to Micropy via `micropy stubs search <QUERY>`

Alternatively, using `micropy stubs add <PATH>`, you can manually add stubs to Micropy.
For manual stub generation, please see [Josvel/micropython-stubber](https://github.com/Josverl/micropython-stubber).

### Creating Stubs

Using `micropy stubs create <PORT/IP_ADDRESS>`, MicropyCli can automatically generate and add stubs from any Micropython device you have on hand. This can be done over both USB and WiFi.

> Note: For stub creation, micropy-cli has additional dependencies.
>
> These can be installed by executing: `pip install micropy-cli[create_stubs]`


### Viewing Stubs

To list stubs you have installed, simply run `micropy stubs list`.

To search for stubs for your device, use `micropy stubs search <QUERY>`.

# See Also

* [VSCode IntelliSense, Autocompletion & Linting capabilities](https://lemariva.com/blog/2019/08/micropython-vsc-ide-intellisense)
    - An awesome article written by [lemariva](https://github.com/lemariva). It covers creating a micropython project environment from scratch using `micropy-cli` and [pymakr-vsc](pymakr-vsc). Great place to start if you're new to this!


# Acknowledgements

## Micropython-Stubber
[Josvel/micropython-stubber](https://github.com/Josverl/micropython-stubber)

Josverl's Repo is full of information regarding Micropython compatibility with VSCode and more. To find out more about how this process works, take a look at it.

micropy-cli and [micropy-stubs](https://github.com/BradenM/micropy-stubs) depend on micropython-stubber for its ability to generate frozen modules, create stubs on a pyboard, and more.


================================================
FILE: docs/cli.rst
================================================
CLI Usage
=====

.. click:: micropy.cli:cli
   :prog: micropy

.. click:: micropy.cli:init
   :prog: micropy init
   :show-nested:

.. click:: micropy.cli:stubs
   :prog: micropy stubs
   :show-nested:

.. click:: micropy.cli:install
   :prog: micropy install
   :show-nested:


================================================
FILE: docs/conf.py
================================================
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html

# -- Path setup --------------------------------------------------------------

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#

import os
import sys

from recommonmark.transform import AutoStructify

try:
    import importlib.metadata as importlib_metadata
except ModuleNotFoundError:
    import importlib_metadata

sys.path.insert(0, os.path.abspath(".."))


source_suffix = [".rst", ".md"]

# -- Project information -----------------------------------------------------

project = "micropy-cli"
copyright = "2021, Braden Mars"
author = "Braden Mars"

github_doc_root = "https://github.com/BradenM/micropy-cli/tree/master/docs/"

# The full version, including alpha/beta/rc tags
release = importlib_metadata.version("micropy-cli")


# -- General configuration ---------------------------------------------------

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
    "sphinx.ext.autodoc",
    "sphinx.ext.coverage",
    "sphinx.ext.napoleon",
    "sphinx_autodoc_typehints",
    "sphinx.ext.autosummary",
    "sphinx.ext.autosectionlabel",
    "sphinx_click.ext",
    "recommonmark",
]

autodoc_default_flags = ["members", "show-inheritance"]  # Defaults
autosummary_generate = True  # Enable Autosummary
autosummary_imported_members = True
autosectionlabel_prefix_document = True

# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]


# -- Options for HTML output -------------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
#
html_theme = "sphinx_rtd_theme"

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["_static"]


# At the bottom of conf.py


def setup(app):
    app.add_config_value(
        "recommonmark_config",
        {
            "url_resolver": lambda url: github_doc_root + url,
            "auto_toc_tree_section": "Contents",
            "enable_eval_rst": True,
        },
        True,
    )
    app.add_transform(AutoStructify)


================================================
FILE: docs/header.rst
================================================
Micropy Cli |PyPI| |PyPI - Python Version| |Github - Test Micropy Cli| |Coverage Status|
========================================================================================

Micropy Cli is a project management/generation tool for writing
`Micropython`_ code in modern IDEs such as VSCode. Its primary goal is
to automate the process of creating a workspace complete with:

-  **Linting** compatible with Micropython
-  VSCode **Intellisense**
-  **Autocompletion**
-  Dependency Management
-  VCS Compatibility

.. figure:: ../.github/img/micropy.svg
   :width: 100%


Installation
------------

You can download and install the latest version of this software from
the Python package index (PyPI) as follows:

``pip install --upgrade micropy-cli``


.. _Micropython: https://micropython.org/

.. |PyPI| image:: https://img.shields.io/pypi/v/micropy-cli?logo=pypi&logoColor=white&style=flat-square
   :target: https://pypi.org/project/micropy-cli/
.. |PyPI - Python Version| image:: https://img.shields.io/pypi/pyversions/micropy-cli.svg?style=flat-square&logo=python&logoColor=green
   :target: https://pypi.org/project/micropy-cli/
.. |Github - Test Micropy Cli| image:: https://img.shields.io/github/workflow/status/BradenM/micropy-cli/Test%20MicropyCli/master?logo=github&style=flat-square
   :target: https://github.com/BradenM/micropy-cli/actions
.. |Coverage Status| image:: https://img.shields.io/coveralls/github/BradenM/micropy-cli/master?style=flat-square&logo=coveralls
   :target: https://coveralls.io/github/BradenM/micropy-cli


================================================
FILE: docs/index.rst
================================================
.. include:: header.rst

.. toctree::
   :caption: Documentation
   :maxdepth: 2

   base.md
   cli
   modules



Indices and tables
==================

* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`


================================================
FILE: docs/modules.rst
================================================
API Reference
=============

.. autosummary::
   :toctree: _autosummary

   micropy
   micropy.main
   micropy.exceptions
   micropy.stubs
   micropy.stubs.source
   micropy.project
   micropy.project.modules
   micropy.utils
   micropy.config
   micropy.config.config_source
   micropy.packages


================================================
FILE: micropy/__init__.py
================================================
"""Micropy Cli.

Micropy Cli is a project management/generation tool for writing Micropython
code in modern IDEs such as VSCode. Its primary goal is to automate the
process of creating a workspace complete with:

Linting compatible with Micropython,
VSCode Intellisense,
Autocompletion,
Dependency Management,
VCS Compatibility
and more.

"""

from __future__ import annotations

from micropy.main import MicroPy
from micropy.utils._compat import metadata

__version__ = metadata.version("micropy-cli")


================================================
FILE: micropy/__main__.py
================================================
from __future__ import annotations

import sys

if __name__ == "__main__":
    from micropy.cli import cli

    sys.exit(cli())


================================================
FILE: micropy/app/__init__.py
================================================
from .main import app

__all__ = ["app"]


================================================
FILE: micropy/app/main.py
================================================
from __future__ import annotations

from enum import Enum
from pathlib import Path
from typing import List, Optional, cast

import micropy.exceptions as exc
import questionary as prompt
import typer
from micropy import logger, utils
from micropy.main import MicroPy
from micropy.project import Project, modules
from micropy.stubs.stubs import Stub
from micropy.utils._compat import metadata
from questionary import Choice

from .stubs import stubs_app

app = typer.Typer(name="micropy-cli", no_args_is_help=True, rich_markup_mode="markdown")
app.add_typer(stubs_app)


@app.callback()
def main_callback(ctx: typer.Context):
    """
    **Micropy CLI** is a project management/generation tool for writing [Micropython](https://micropython.org/) code in modern IDEs such as VSCode.

    Its primary goal is to automate the process of creating a workspace complete with:

    * **Linting** compatible with Micropython

    * IDE **Intellisense**

    * **Autocompletion**

    * Dependency Management

    * VCS Compatibility
    """
    if ctx.resilient_parsing:
        return
    micropy = ctx.ensure_object(MicroPy)
    if not micropy.project.exists:
        return
    latest = utils.is_update_available()
    if latest:
        log = logger.Log.get_logger("MicroPy")
        log.title("Update Available!")
        log.info(f"Version $B[v{latest}] is now available")
        log.info("You can update via: $[pip install --upgrade micropy-cli]\n")


@app.command(name="version")
def main_version():
    """Print Micropy CLI Version."""
    vers = metadata.version("micropy-cli")
    print(f"Micropy Version: {vers}")
    raise typer.Exit()


TemplateEnum = Enum(
    "TemplateEnum", {t: t for t in list(modules.TemplatesModule.TEMPLATES.keys())}, type=str
)


def template_callback(
    ctx: typer.Context, value: Optional[List[TemplateEnum]]
) -> Optional[List[TemplateEnum]]:
    if ctx.resilient_parsing:
        return
    if not value:
        templates = modules.TemplatesModule.TEMPLATES.items()
        templ_choices = [Choice(str(val[1]), value=t) for t, val in templates]
        value = prompt.checkbox("Choose any Templates to Generate", choices=templ_choices).ask()
        if not value:
            if not prompt.confirm(
                "You have chosen to use NO templates. Are you sure you want to continue?",
                default=False,
            ).ask():
                raise typer.Abort()
            return []
    value = [TemplateEnum(k) for k in value]
    return value


def path_callback(ctx: typer.Context, value: Optional[Path]) -> Optional[Path]:
    if ctx.resilient_parsing:
        return
    return value if value else Path.cwd()


def name_callback(ctx: typer.Context, value: Optional[str]) -> Optional[str]:
    if ctx.resilient_parsing:
        return
    if not value:
        path = ctx.params.get("path", Path.cwd())
        default_name = path.name
        prompt_name = prompt.text("Project Name", default=default_name).ask()
        if prompt_name is None:
            raise typer.Abort("You must provide a project name via prompt or --name option.")
        return prompt_name.strip()
    return value


def stubs_callback(ctx: typer.Context, value: Optional[List[str]]) -> Optional[List[Stub]]:
    if ctx.resilient_parsing:
        return
    mpy = ctx.ensure_object(MicroPy)
    stub_values = (
        [i for i in [mpy.stubs.add(s) for s in value] if i is not None]
        if value
        else list(mpy.stubs)
    )
    if not stub_values:
        mpy.log.error("You don't have any stubs!")
        mpy.log.title("To add stubs to micropy, use $[micropy stubs add <STUB_NAME>]")
        mpy.log.info("See: $[micropy stubs --help] for more information.")
        raise typer.Abort(1)
    if not value:
        # if value was not explicitly provided, ask for selections.
        stubs = [Choice(str(s), value=s) for s in stub_values]
        stub_choices = prompt.checkbox("Which stubs would you like to use?", choices=stubs).ask()
        if not stub_choices:
            # mpy.log.error("You must choose at least one stub!")
            raise typer.BadParameter(
                "You must choose at least one stub!",
                ctx,
            )
        return stub_choices
    return stub_values


@app.command(name="init")
def main_init(
    ctx: typer.Context,
    path: Optional[Path] = typer.Argument(
        None,
        help="Path to project. Defaults to current working directory.",
        callback=path_callback,
        dir_okay=True,
        file_okay=False,
        show_default=False,
    ),
    name: Optional[str] = typer.Option(
        None,
        "--name",
        "-n",
        help="Project Name. Defaults to path name.",
        show_default=False,
        callback=name_callback,
    ),
    template: Optional[List[TemplateEnum]] = typer.Option(
        None,
        "--template",
        "-t",
        help="Templates to generate for project. Can be specified multiple times. Skips interactive prompt.",
        show_default=False,
        callback=template_callback,
    ),
    stubs: Optional[List[str]] = typer.Option(
        None,
        "--stubs",
        "-s",
        help="Name of stubs to add to project. Can be specified multiple times. Skips interactive prompt.",
        callback=stubs_callback,
        show_default=False,
    ),
):
    """Create new Micropython Project.

    \b When creating a new project, all files will be placed under the
    generated <PROJECT_NAME> folder.

    """
    mpy: MicroPy = ctx.find_object(MicroPy)
    mpy.log.title("Creating New Project")
    # weird issue where "template" from args
    # gets set a [None,None], but its correct in params.
    template = ctx.params.get("template", template)
    project = Project(path, name=name)
    project.add(modules.StubsModule, mpy.stubs, stubs=stubs)
    project.add(modules.PackagesModule, "requirements.txt")
    project.add(modules.DevPackagesModule, "dev-requirements.txt")
    project.add(
        modules.TemplatesModule,
        templates=[t.value for t in template if t],
        run_checks=mpy.RUN_CHECKS,
    )
    proj_path = project.create()
    try:
        rel_path = f"./{proj_path.relative_to(Path.cwd())}"
    except ValueError:
        rel_path = proj_path
    mpy.log.title(f"Created $w[{project.name}] at $w[{rel_path}]")


def ensure_project(ctx: typer.Context) -> Project:
    mpy = ctx.ensure_object(MicroPy)
    project = mpy.project
    if not project.exists:
        mpy.log.error("You are not currently in an active project!")
        raise typer.Abort(1)
    # todo: fix type issue.
    return cast(Project, project)


def install_local_callback(ctx: typer.Context, value: Optional[Path]) -> Optional[Path]:
    """Handle package installation from local path."""
    if ctx.resilient_parsing:
        return
    if value is None:
        return value
    mpy = ctx.ensure_object(MicroPy)
    project = ensure_project(ctx)
    pkg_name = next(iter(ctx.args), None)
    mpy.log.title("Installing Local Package")
    pkg_path = "-e " + str(value)
    project.add_package(pkg_path, dev=ctx.params.get("dev", False), name=pkg_name)
    raise typer.Exit()


def install_project_callback(ctx: typer.Context, value: Optional[List[str]]) -> Optional[List[str]]:
    """Handle project requirements install."""
    if ctx.resilient_parsing:
        return
    if "path" in ctx.params:
        return
    if not value:
        # only if no packages are provided.
        mpy = ctx.ensure_object(MicroPy)
        project = ensure_project(ctx)
        mpy.log.title("Installing all Requirements")
        try:
            project.add_from_file(dev=ctx.params.get("dev", False))
        except Exception as e:
            mpy.log.error("Failed to load requirements!", exception=e)
            raise typer.Abort() from e
        else:
            mpy.log.success("\nRequirements Installed!")
            raise typer.Exit()
    return value


@app.command(name="install")
def main_install(
    ctx: typer.Context,
    packages: Optional[List[str]] = typer.Argument(
        None, help="Packages to install.", callback=install_project_callback
    ),
    dev: bool = typer.Option(
        default=False,
        help="Install as development package. This will not generate stubs for the package.",
        show_default=True,
    ),
    path: Optional[Path] = typer.Option(
        None,
        help="Add dependency from local path. Can be a file or directory.",
        callback=install_local_callback,
    ),
):
    """Install Packages as Project Requirements.

    \b
    Install a project dependency while enabling
    intellisense, autocompletion, and linting for it.

        \b
        $ micropy install picoweb==1.8.2 blynklib
        \b



    \b
    If no packages are passed and a requirements.txt file is found,
    then micropy will install all packages listed in it.

    \b
    If the --dev flag is passed, then the packages are only
    added to micropy.json. They are not stubbed.

    \b
    To add a dependency from a path, use the --path option
    and provide a name for your package:

        \b
        $ micropy install --path ./src/lib/mypackage MyCustomPackage
        \b



    \b
    You can import installed packages just as you would
    on your actual device:

        \b
        _import <package_name>_

    """
    mpy: MicroPy = ctx.ensure_object(MicroPy)
    project = ensure_project(ctx)
    mpy.log.title("Installing Packages")
    for pkg in packages:
        try:
            project.add_package(pkg, dev=dev)
        except exc.RequirementException as e:
            pkg_name = str(e.package)
            mpy.log.error(f"Failed to install {pkg_name}!" " Is it available on PyPi?", exception=e)
            raise typer.Abort() from e


================================================
FILE: micropy/app/stubs.py
================================================
from __future__ import annotations

import sys
import tempfile
from enum import Enum
from pathlib import Path
from typing import List, Optional, Type

import micropy.exceptions as exc
import typer
from micropy.exceptions import PyDeviceError
from micropy.logger import Log
from micropy.main import MicroPy
from micropy.pyd import (
    DevicePath,
    MessageHandlers,
    MetaPyDeviceBackend,
    ProgressStreamConsumer,
    PyDevice,
)
from micropy.pyd.backend_rshell import RShellPyDeviceBackend
from micropy.pyd.backend_upydevice import UPyDeviceBackend
from micropy.stubs import source as stubs_source
from micropy.utils.stub import prepare_create_stubs
from stubber.codemod import board as stub_board
from stubber.codemod.modify_list import ListChangeSet

stubs_app = typer.Typer(name="stubs", rich_markup_mode="markdown", no_args_is_help=True)


@stubs_app.callback()
def stubs_callback():
    """Manage Micropy Stubs.

    \b
    Stub files are what enable linting,
    Intellisense, Autocompletion, and more.

    \b
    To achieve the best results, you can install
    stubs specific to your device/firmware using:

     -  *micropy stubs search* `STUB_NAME`

     -  *micropy stubs add* `STUB_NAME`



    For more info, please check micropy stubs add --help
    """
    pass


class CreateBackend(str, Enum):
    upydevice = ("upydevice", UPyDeviceBackend)
    rshell = ("rshell", RShellPyDeviceBackend)

    def __new__(cls, value: str, backend: Type[MetaPyDeviceBackend]):
        obj = str.__new__(cls, value)
        obj._value_ = value
        obj.backend = backend
        return obj


def create_changeset(
    value: Optional[List[str]], *, replace: bool = False
) -> Optional[ListChangeSet]:
    if value is None:
        return value
    return ListChangeSet.from_strings(add=value, replace=replace)


@stubs_app.command(name="create")
def stubs_create(
    ctx: typer.Context,
    port: str = typer.Argument(..., help="Serial port used to connect to device"),
    backend: CreateBackend = typer.Option(CreateBackend.upydevice, help="PyDevice backend to use."),
    variant: stub_board.CreateStubsVariant = typer.Option(
        stub_board.CreateStubsVariant.BASE,
        "-v",
        "--variant",
        help="Create Stubs variant.",
        rich_help_panel="Stubs",
    ),
    module: Optional[List[str]] = typer.Option(
        None,
        "-m",
        "--module",
        help="Modules to look for and stub. This flag can be used multiple times.",
        rich_help_panel="Stubs",
    ),
    module_defaults: bool = typer.Option(
        True, help="Include createstubs.py default modules.", rich_help_panel="Stubs"
    ),
    exclude: Optional[List[str]] = typer.Option(
        None,
        "-e",
        "--exclude",
        help="Modules to exclude from stubber. This flag can be used multiple times.",
        rich_help_panel="Stubs",
    ),
    exclude_defaults: bool = typer.Option(
        True,
        help="Include createstubs.py default module excludes. This flag can be used multiple times.",
        rich_help_panel="Stubs",
    ),
    compile: bool = typer.Option(
        True,
        "-c",
        "--compile",
        help="Cross compile to .mpy via mpy-cross.",
        rich_help_panel="Stubs",
    ),
):
    """Create stubs from micropython-enabled devices.

    Utilize Josverl's [micropython-stubber](https://github.com/josverl/micropython-stubber/)
    to generate stubs from your own micropython-enabled device.

    \n
    **Create stubs with defaults**:\n
     - `micropy stubs create /dev/ttyUSB0`

    \n
    **Specify additional modules**:\n
     - `micropy stubs create -m custom_module -m other_module /dev/ttyUSB0`\n
     - _Only given modules_: `micropy stubs create -m custom_module --no-module-defaults /dev/ttyUSB0`

    \n
    **Exclude additional modules**:\n
     - `micropy stubs create -e custom_module -e other_module /dev/ttyUSB0`\n
     - _Only exclude given modules_: `micropy stubs create -e custom_module --no-module-defaults /dev/ttyUSB0`

    \n
    **Create Stubs Variants**:\n
     - **mem**: Optimized for low memory devices._\n
     - **db**: Persist stub progress across reboots.\n
     - **lvgl**: Additional support for LVGL devices.\n

    """
    mp: MicroPy = ctx.ensure_object(MicroPy)
    log = mp.log
    log.title(f"Connecting to Pyboard @ $[{port}]")
    pyb_log = Log.add_logger("Pyboard", "bright_white")

    def _get_desc(name: str, cfg: dict):
        desc = f"{pyb_log.get_service()} {name}"
        return name, cfg | dict(desc=desc)

    message_handler = MessageHandlers(
        on_message=lambda x: isinstance(x, str) and pyb_log.info(x.strip())
    )
    try:
        pyb = PyDevice(
            port,
            auto_connect=True,
            stream_consumer=ProgressStreamConsumer(on_description=_get_desc),
            message_consumer=message_handler,
            backend=backend.backend,
        )
    except (SystemExit, PyDeviceError):
        log.error(f"Failed to connect, are you sure $[{port}] is correct?")
        return None

    log.success("Connected!")
    if module or exclude:
        log.title("Preparing createstubs for:")
        log.info(f"Modules: {', '.join(module or [])}")
        log.info(f"Exclude: {', '.join(exclude or [])}")
    create_stubs = prepare_create_stubs(
        variant=variant,
        modules_set=create_changeset(module, replace=not module_defaults),
        exclude_set=create_changeset(exclude, replace=not exclude_defaults),
        compile=compile,
    )
    dev_path = DevicePath("createstubs.mpy") if compile else DevicePath("createstubs.py")
    log.info("Executing stubber on pyboard...")
    try:
        pyb.run_script(create_stubs, DevicePath(dev_path))
    except Exception as e:
        # TODO: Handle more usage cases
        log.error(f"Failed to execute script: {str(e)}", exception=e)
        raise
    log.success("Done!")
    log.info("Copying stubs...")
    with tempfile.TemporaryDirectory() as tmpdir:
        pyb.copy_from(
            DevicePath("/stubs"),
            tmpdir,
            verify_integrity=True,
            # exclude due to ps1 var possibly different.
            exclude_integrity={"sys.py", "usys.py"},
        )
        out_dir = Path(tmpdir)
        stub_path = next(out_dir.iterdir())
        log.info(f"Copied Stubs: $[{stub_path.name}]")
        stub_path = mp.stubs.from_stubber(stub_path, out_dir)
        stub = mp.stubs.add(str(stub_path))
    pyb.remove(dev_path)
    pyb.disconnect()
    log.success(f"Added {stub.name} to stubs!")
    return stub


@stubs_app.command(name="add")
def stubs_add(ctx: typer.Context, stub_name: str, force: bool = False):
    """Add Stubs from package or path.

    \b
    In general, stub package names follow this schema:
        <device>-<firmware>-<version>

    \b
    For example:
        esp32-micropython-1.11.0

    \b
    You can search premade stub packages using:
        micropy stubs search <QUERY>

    Checkout the docs on Github for more info.

    """
    mpy: MicroPy = ctx.find_object(MicroPy)
    proj = mpy.project
    mpy.log.title(f"Adding $[{stub_name}] to stubs")
    locator = stubs_source.StubSource(
        [stubs_source.RepoStubLocator(mpy.repo), stubs_source.StubInfoSpecLocator()]
    )
    with locator.ready(stub_name) as stub:
        stub_name = stub
    try:
        stub = mpy.stubs.add(stub_name, force=force)
    except exc.StubNotFound:
        mpy.log.error(f"$[{stub_name}] could not be found!")
        sys.exit(1)
    except exc.StubError:
        mpy.log.error(f"$[{stub_name}] is not a valid stub!")
        sys.exit(1)
    else:
        mpy.log.success(f"{stub.name} added!")
        if proj.exists:
            mpy.log.title(f"Adding $[{stub.name}] to $[{proj.name}]")
            proj.add_stub(stub)


@stubs_app.command(name="search")
def stubs_search(ctx: typer.Context, query: str, show_outdated: bool = False):
    """Search available stubs."""
    mpy: MicroPy = ctx.find_object(MicroPy)
    installed_stubs = map(str, mpy.stubs._loaded | mpy.stubs._firmware)
    results = [
        (r, r.name in installed_stubs)
        for r in mpy.repo.search(query, include_versions=show_outdated)
    ]
    results = sorted(results, key=lambda pkg: pkg[0].name)
    if not any(results):
        mpy.log.warn(f"No results found for: $[{query}].")
        sys.exit(0)
    mpy.log.title(f"Results for $[{query}]:")
    max_name = max(len(n[0].repo_name) for n in results)
    for pkg, installed in results:
        pad = max_name - len(pkg.repo_name) + 2
        pad = pad if (pad % 2 == 0) else pad + 1
        spacer = "{:>{pad}}".format("::", pad=pad)
        repo_logger = Log.add_logger(f"{pkg.repo_name} {spacer}", "bright_white")
        name = "{:>{pad}}".format(f"{pkg.name} ($w[{pkg.version}])", pad=pad)
        name = f"{name} $B[(Installed)]" if installed else name
        repo_logger.info(name)


@stubs_app.command(name="list")
def stubs_list(ctx: typer.Context):
    """List installed stubs."""
    mpy: MicroPy = ctx.find_object(MicroPy)

    def print_stubs(stub_list):
        for firm, stubs in stub_list:
            if stubs:
                title = str(firm).capitalize()
                mpy.log.title(f"$[{title}]:")
                for stub in stubs:
                    mpy.log.info(str(stub))

    mpy.log.title("Installed Stubs:")
    mpy.log.info(f"Total: {len(mpy.stubs)}")
    print_stubs(mpy.stubs.iter_by_firmware())
    mpy.verbose = False
    proj = mpy.project
    if proj.exists:
        mpy.log.title(f"Stubs used in {proj.name}:")
        mpy.log.info(f"Total: {len(proj.stubs)}")
        stubs = mpy.stubs.iter_by_firmware(stubs=proj.stubs)
        print_stubs(stubs)


================================================
FILE: micropy/config/__init__.py
================================================
"""Configuration files and interfaces for them."""

from .config import Config
from .config_dict import DictConfigSource
from .config_json import JSONConfigSource

__all__ = ["Config", "JSONConfigSource", "DictConfigSource"]


================================================
FILE: micropy/config/config.py
================================================
from copy import deepcopy
from typing import Any, List, Optional, Sequence, Tuple, Type, Union

import dpath
from boltons import iterutils
from micropy.logger import Log, ServiceLog

from .config_json import JSONConfigSource
from .config_source import ConfigSource

"""Config Interface"""


class Config:
    """Configuration File Interface.

    Automatically syncs config in memory
    with config saved to disk.

    Args:
        path (Path): Path to save file at.
        source_format (ConfigSource, optional): Configuration File Format.
            Defaults to JSONConfigSource.
        default (dict, optional): Default configuration.
                Defaults to {}.

    """

    def __init__(
        self,
        *args: Any,
        source_format: Type[ConfigSource] = JSONConfigSource,
        default: Optional[dict] = None,
    ):
        default = default or dict()
        self.log: ServiceLog = Log.add_logger(f"{__name__}")
        self.format = source_format
        self._source: ConfigSource = self.format(*args)
        self._config = deepcopy(default)
        if self._source.exists:
            with self._source as src:
                self.log.debug("loaded config values")
                dpath.util.merge(self._config, src, flags=dpath.MERGE_REPLACE)

    @property
    def source(self) -> ConfigSource:
        return self._source

    @source.setter
    def source(self, value: Any) -> ConfigSource:
        self._source = self.format(value)
        return self._source

    @property
    def config(self) -> dict:
        return self._config

    def raw(self) -> dict:
        return self._config

    def sync(self) -> dict:
        """Sync in-memory config with disk.

        Returns:
            dict: updated config

        """
        with self.source as src:
            dpath.util.merge(src, self.config, flags=dpath.MERGE_REPLACE)
        return self.config

    def parse_key(self, key: str) -> Tuple[Sequence[str], str]:
        """Parses key.

        Splits it into a path and 'final key'
        object. Each key is separates by a: "/"

        Example:
            >>> self.parse_key('item/subitem/value')
            (('item', 'subitem'), 'value')

        Args:
            key (str): key in dot notation

        Returns:
            Tuple[Sequence[str], str]: Parsed key

        """
        full_path = tuple(i for i in key.split("/"))
        path = full_path[:-1]
        p_key = full_path[-1]
        return (path, p_key)

    def get(self, key: str, default: Any = None) -> Any:
        """Retrieve config value.

        Args:
            key (str): Key (in dot-notation) of value to return.
            default (Any, optional): Default value to return.
                Defaults to None.

        Returns:
            Any: Value at key given

        """
        try:
            value = dpath.util.get(self.config, key)
        except KeyError:
            value = default
        else:
            return value
        return value

    def set(self, key: str, value: Any) -> Any:
        """Set config value.

        Args:
            key (str): Key (in dot-notation) to update.
            value (Any): Value to set

        Returns:
            Any: Updated config

        """
        dpath.set(self._config, key, value)
        self.log.debug(f"set config value [{key}] => {value}")
        return self.sync()

    def add(self, key: str, value: Any) -> Any:
        """Overwrite or add config value.

        Args:
            key: Key to set
            value: Value to add or update too

        Returns:
            Updated config

        """
        dpath.new(self._config, key, value)
        self.log.debug(f"added config value [{key}] -> {value}")
        return self.sync()

    def pop(self, key: str) -> Any:
        """Delete and return value at key.

        Args:
            key (str): Key to pop.

        Returns:
            Any: Popped value.

        """
        path, target = self.parse_key(key)
        value = self.get(key)
        remapped = iterutils.remap(
            self._config, lambda p, k, v: False if p == path and k == target else True
        )
        self._config = remapped
        self.log.debug(f"popped config value {value} <- [{key}]")
        return self.sync()

    def extend(self, key: str, value: List[Any], unique: bool = False) -> dict:
        """Extend a list in config at key path.

        Args:
            key: Key to path to extend.
            value: List of values to extend by.
            unique: Only extend values if not already in values.

        Returns:
            Updated Config

        """
        to_update = list(deepcopy(self.get(key, value)))
        if unique:
            value = [v for v in value if v not in to_update]
        dpath.merge(to_update, value, flags=dpath.MERGE_ADDITIVE)
        self.set(key, to_update)
        return self.sync()

    def upsert(self, key: str, value: Union[List[Any], dict]) -> dict:
        """Update or insert values into key list or dict.

        Args:
            key: Key to value to upsert.
            value: Value to upsert by.

        Returns:
            Updated config.

        """
        to_update = deepcopy(self.get(key, value))
        dpath.merge(to_update, value, flags=dpath.MERGE_REPLACE)
        self.add(key, to_update)
        return self.sync()

    def search(self, key):
        """Retrieve all values at key (with glob pattern).

        Args:
            key: Key with pattern to search with.

        Returns:
            Values matching key and pattern.

        """
        return dpath.values(self.config, key)


================================================
FILE: micropy/config/config_dict.py
================================================
from typing import Optional

from .config_source import ConfigSource


class DictConfigSource(ConfigSource):
    def __init__(self, config: Optional[dict] = None):
        """Dict Config Source.

        Args:
            config (dict, optional): Initial Config.
                Defaults to {}.

        """
        super().__init__(initial_config=config)

    @property
    def exists(self) -> bool:
        return any(self.config.keys())

    def process(self) -> dict:
        return self.config

    def prepare(self):
        return super().prepare()

    def save(self, content: dict) -> dict:
        return content


================================================
FILE: micropy/config/config_json.py
================================================
import json
from pathlib import Path

from boltons.fileutils import AtomicSaver

from .config_source import ConfigSource


class JSONConfigSource(ConfigSource):
    """JSON Config File Source.

    Args:
        path (Path): Path to save config too.

    """

    def __init__(self, path: Path):
        super().__init__()
        self._file_path: Path = path

    @property
    def file_path(self) -> Path:
        """Path to config file."""
        return self._file_path

    @file_path.setter
    def file_path(self, value: Path) -> Path:
        """Set config file path.

        Args:
            value (Path): New path to config file

        Returns:
            Path: Path to config file

        """
        self._file_path = value
        return self._file_path

    @property
    def exists(self) -> bool:
        return self.file_path.exists()

    def process(self) -> dict:
        """Load config from JSON file.

        Returns:
            dict: config in file

        """
        content = self.file_path.read_text()
        if not content:
            return {}
        config = json.loads(content)
        return config

    def prepare(self):
        if not self.file_path.exists():
            self.log.debug(f"creating new config file: {self.file_path}")
            self.file_path.parent.mkdir(parents=True, exist_ok=True)
            self.file_path.touch()

    def save(self, content: dict) -> Path:
        """Save current config.

        Args:
            content (dict): content to write to file.

        Returns:
            Path: path to config file.

        """
        config = json.dumps(content, indent=4, separators=(",", ": "))
        with AtomicSaver((str(self.file_path)), text_mode=True) as file:
            file.write(config)
        return self.file_path


================================================
FILE: micropy/config/config_source.py
================================================
"""Config Abstract."""

import abc
import contextlib
from typing import Any, Optional

from micropy.logger import Log, ServiceLog


class ConfigSource(contextlib.AbstractContextManager, metaclass=abc.ABCMeta):
    """Abstract Base Class for Config Sources.

    Args:
        initial_config (dict, optional): Initial config values.
            Defaults to {}.

    """

    def __init__(self, initial_config: Optional[dict] = None):
        self._config: dict = initial_config or dict()
        self.log: ServiceLog = Log.add_logger(__name__)

    @property
    def config(self) -> dict:
        """Current Config Content."""
        return self._config

    @config.setter
    def config(self, value: dict) -> dict:
        """Set current config content.

        Args:
            value (dict): New value to set

        Returns:
            dict: Current config

        """
        self._config = value
        return self._config

    @abc.abstractproperty
    def exists(self) -> bool:
        """Property to check if source exists."""

    @abc.abstractmethod
    def save(self, content: Any) -> Any:
        """Method to save config."""

    @abc.abstractmethod
    def process(self) -> dict:
        """Read and process config file.

        Returns:
            dict: Config file content

        """

    @abc.abstractmethod
    def prepare(self) -> Any:
        """Method to prepare on enter."""

    def __enter__(self) -> dict:
        self.prepare()
        self._config = self.process()
        return self._config

    def __exit__(self, *args):
        self.save(self._config)


================================================
FILE: micropy/data/__init__.py
================================================
"""
micropy.data
~~~~~~~~~~~~~~

This module is merely to provide an easy method of locating
data files used by MicropyCli
"""

from pathlib import Path

__all__ = ["ROOT", "SCHEMAS", "REPO_SOURCES", "FILES", "STUB_DIR", "LOG_FILE", "STUBBER"]

# Paths
MOD_PATH = Path(__file__).parent
PATH = MOD_PATH.absolute()
ROOT = MOD_PATH.parent.absolute()

# Stub Schemas
SCHEMAS = PATH / "schemas"

# Default Stub Sources
REPO_SOURCES = PATH / "sources.json"

# Application Data
FILES = Path.home() / ".micropy"
STUB_DIR = FILES / "stubs"
LOG_FILE = FILES / "micropy.log"

# Libraries
LIB = ROOT / "lib"
STUBBER = LIB / "stubber"


================================================
FILE: micropy/data/schemas/firmware.json
================================================
{
    "type": "object",
    "required": [
        "name",
        "repo",
        "firmware",
        "modules",
        "devices",
        "versions"
    ],
    "properties": {
        "scope": {
            "type": "string"
        },
        "name": {
            "type": "string"
        },
        "repo": {
            "type": "string"
        },
        "module_path": {
            "type": ["string", "array"]
        },
        "firmware": {
            "type": "string"
        },
        "excluded_modules": {
            "type": "array",
            "items": {
                "type": "string"
            }
        },
        "modules": {
            "type": "array",
            "items": {
                "type": "string"
            }
        },
        "devices": {
            "type": "array",
            "items": {
                "type": "string"
            }
        },
        "path": {
            "type": "string"
        },
        "versions": {
            "type": "array",
            "items": {
                "type": "object",
                "required": ["version", "git_tag", "sha", "latest", "devices"],
                "properties": {
                    "version": {
                        "type": "string"
                    },
                    "git_tag": {
                        "type": "string"
                    },
                    "sha": {
                        "type": "string"
                    },
                    "latest": {
                        "type": "boolean",
                        "default": false
                    },
                    "devices": {
                        "type": "array",
                        "items": {
                            "type": "string"
                        }
                    }
                }
            }
        }
    }
}


================================================
FILE: micropy/data/schemas/stubs.json
================================================
{
    "type": "object",
    "required": ["firmware", "modules"],
    "properties": {
        "firmware": {
            "type": "object",
            "required": [
                "nodename",
                "version",
                "sysname"
            ],
            "properties": {
                "family": {
                    "type": "string"
                },
                "machine": {
                    "type": "string"
                },
                "firmware": {
                    "type": "string"
                },
                "nodename": {
                    "type": "string"
                },
                "version": {
                    "type": "string"
                },
                "release": {
                    "type": "string"
                },
                "sysname": {
                    "type": "string"
                },
                "name": {
                    "type": "string"
                }
            }
        },
        "stubber": {
            "$id": "#/properties/stubber",
            "type": "object",
            "title": "The Stubber Schema",
            "required": ["version"],
            "properties": {
                "version": {
                    "type": "string"
                }
            }
        },
        "modules": {
            "$id": "#/properties/modules",
            "type": "array",
            "title": "The Modules Schema",
            "items": {
                "type": "object",
                "required": ["file", "module"],
                "properties": {
                    "file": {
                        "type": "string"
                    },
                    "module": {
                        "type": "string"
                    }
                }
            }
        }
    }
}


================================================
FILE: micropy/data/sources.json
================================================
[
    {
        "display_name": "BradenM/micropy-stubs",
        "name": "micropy-stubs",
        "source": "https://raw.githubusercontent.com/BradenM/micropy-stubs/master/source.json"
    },
    {
        "display_name": "Josverl/micropython-stubs",
        "name": "micropython-stubs",
        "source": "https://raw.githubusercontent.com/Josverl/micropython-stubs/main/publish/package_data.jsondb"
    }
]


================================================
FILE: micropy/exceptions.py
================================================
"""Micropy Exceptions."""
from __future__ import annotations


class MicropyException(Exception):
    """Generic MicroPy Exception."""


class StubError(MicropyException):
    """Exception for any errors raised by stubs."""

    def __init__(self, message=None, stub=None):
        super().__init__(message)
        self.stub = stub
        self.message = message
        if message is None:
            message = "An error occurred with this stub."


class StubValidationError(StubError):
    """Raised when a stub fails validation."""

    def __init__(self, path, errors, *args, **kwargs):
        msg = f"Stub at[{str(path)}] encountered" f" the following validation errors: {str(errors)}"
        super().__init__(msg, *args, **kwargs)

    def __str__(self):
        return self.message


class StubNotFound(StubError):
    """Raised when a stub cannot be found."""

    def __init__(self, stub_name=None):
        stub_name = stub_name or "Unknown"
        msg = f"{stub_name} is not available!"
        super().__init__(msg)


class RequirementException(MicropyException):
    """A Requirement Exception Occurred."""

    def __init__(self, *args, **kwargs):
        self.package = kwargs.pop("package", None)
        super().__init__(*args, **kwargs)


class RequirementNotFound(RequirementException):
    """A requirement could not be found."""


class PyDeviceError(MicropyException):
    """Generic PyDevice exception."""

    def __init__(self, message: str = None):
        super().__init__(message)
        self.message = message


class PyDeviceConnectionError(PyDeviceError):
    _default_message = "Failed to connect to pydevice @ {location}."

    def __init__(self, location: str):
        super().__init__(self._default_message.format(location=location))


class PyDeviceFileIntegrityError(PyDeviceError):
    _default_message = (
        "Failed to verify integrity: {device_path} (device={device_sum}, recv={digest})"
    )

    def __init__(self, device_path: str, device_sum: str, digest: str):
        super().__init__(
            self._default_message.format(
                device_path=device_path, device_sum=device_sum, digest=digest
            )
        )


================================================
FILE: micropy/logger.py
================================================
"""Logging functionality.

TODO: Split logging from UI, refactor.

"""

import logging
import re
from contextlib import contextmanager
from logging.handlers import RotatingFileHandler

import click
from micropy import data


class Log:
    """Borg for easy access to any Log from anywhere in the package."""

    __shared_state = {}

    def __init__(self):
        self.__dict__ = self.__shared_state
        self.parent_logger = ServiceLog()
        self.loggers = [self.parent_logger]

    @classmethod
    def add_logger(cls, service_name, base_color="white", **kwargs):
        """Creates a new child ServiceLog instance."""
        _self = cls()
        parent = kwargs.pop("parent", _self.parent_logger)
        logger = ServiceLog(service_name, base_color, parent=parent, **kwargs)
        _self.loggers.append(logger)
        return logger

    @classmethod
    def get_logger(cls, service_name):
        """Retrieves a child logger by service name."""
        _self = cls()
        logger = next(i for i in _self.loggers if i.service_name == service_name)
        return logger


class ServiceLog:
    """Handles logging to stdout and micropy.log.

    :param service_name: Active service to display
    :type service_name: str
    :param base_color: Color of name on output
    :type base_color: str

    """

    LOG_FILE = data.LOG_FILE

    def __init__(self, service_name="MicroPy", base_color="bright_green", **kwargs):
        self.parent = kwargs.get("parent", None)
        self.LOG_FILE.parent.mkdir(exist_ok=True)
        self.base_color = base_color
        self.service_name = service_name
        self.load_handler()
        self.info_color = kwargs.get("info_color", "white")
        self.accent_color = kwargs.get("accent_color", "yellow")
        self.warn_color = kwargs.get("warn_color", "green")
        self.show_title = kwargs.get("show_title", True)
        self.stdout = kwargs.get("stdout", True)

    @contextmanager
    def silent(self):
        self.stdout = False
        yield self
        self.stdout = True

    def load_handler(self):
        """Loads Logging Module Formatting."""
        self.log = logging.getLogger()
        if not self.log.hasHandlers():
            self.log.setLevel(logging.DEBUG)
            self.log_handler = RotatingFileHandler(
                str(self.LOG_FILE),
                mode="a",
                maxBytes=2 * 1024 * 1024,
                backupCount=2,
                encoding=None,
                delay=0,
            )
            self.log_handler.setLevel(logging.DEBUG)
            self.log.addHandler(self.log_handler)
        self.log_handler = self.log.handlers[0]
        log_format = "[%(asctime)s] %(levelname)s: " f"{self.service_name.lower()}: " "%(message)s"
        self.log_handler.setFormatter(logging.Formatter(log_format, "%Y-%m-%d %H:%M:%S"))

    def parse_msg(self, msg, accent_color=None):
        """Parses any color codes accordingly.

        :param str msg:
        :param str accent_color:  (Default value = None)
        :return: Parsed Message
        :rtype: str

        """
        msg_special = re.findall(r"\$(.*?)\[(.*?)\]", msg)
        color = accent_color or self.accent_color
        special = {"fg": color, "bold": True}
        clean = msg
        _parts = re.split(r"\$.*?\[(.*?)\]", msg)
        parts = [(p, None) for p in _parts]
        for w in msg_special:
            if w[0] == "w":
                special["fg"] = self.warn_color
            if w[0] == "B":
                special.pop("fg")
            sindex = _parts.index(w[1])
            parts[sindex] = (w[1], special)
            clean = msg.replace(f"${w[0]}[{w[1]}]", w[1])
        clean = clean.encode("ascii", "ignore").decode("utf-8").strip()
        return (parts, clean)

    def get_parents(self, names=None):
        """Retrieve all parents."""
        names = names or []
        if len(names) == 0:
            names = [self.service_name]
        if self.parent:
            names.insert(0, self.parent.service_name)
            names = self.parent.get_parents(names)
        return names

    def get_service(self, **kwargs):
        """Retrieves formatted service title.

        :param **kwargs:
        :return: formatted title
        :rtype: str

        """
        if not self.show_title:
            return f"{self.parent.get_service(bold=True)}"
        color = kwargs.pop("fg", self.base_color)
        title = click.style(f"{self.service_name}", fg=color, **kwargs)
        title = f"{title}{click.style(' ', fg=color)}"
        if self.parent is not None:
            title = f"{self.parent.get_service(bold=True)} {title}"
        return title

    def iter_formatted(self, message, **kwargs):
        """Iterate formatted message tuple into styled string.

        Args:
            message (tuple): tuple as (msg, style)

        """
        if isinstance(message, str):
            message, _ = self.parse_msg(message)
        for msg in message:
            text, mstyle = msg
            mstyle = mstyle or kwargs
            yield click.style(text, **mstyle)

    def echo(self, msg, **kwargs):
        """Prints msg to stdout.

        :param str msg: message to print
        :param **kwargs:

        """
        title_color = kwargs.pop("title_color", self.base_color)
        title_bold = kwargs.pop("title_bold", True)
        accent_color = kwargs.pop("accent", self.accent_color)
        service_title = self.get_service(fg=title_color, bold=title_bold)
        message, clean = self.parse_msg(msg, accent_color)
        log_attr = kwargs.pop("log", None)
        if log_attr:
            self.load_handler()
            log_func = getattr(logging, log_attr)
            log_func(clean)
        if self.stdout:
            init_msg, init_style = message[0]
            first_part, nl_part, _ = init_msg.partition("\n")
            fp_clean = first_part.encode("ascii", "ignore").decode("unicode_escape")
            if not fp_clean.strip() and nl_part == "\n":
                init_msg = init_msg.replace("\n", "")
                message[0] = (init_msg, init_style)
                click.secho("")
            click.secho(f"{service_title} ", nl=False)
            post_nl = kwargs.pop("nl", None)
            formatted = list(self.iter_formatted(message, **kwargs))
            for msg in formatted:
                do_nl = msg == formatted[-1]
                click.echo(msg, nl=do_nl)
            if post_nl:
                click.echo("")

    def info(self, msg, **kwargs):
        """Prints message with info formatting.

        :param msg:
        :param **kwargs:
        :return: method to print msg
        :rtype: method

        """
        return self.echo(msg, log="info", **kwargs)

    def title(self, msg, **kwargs):
        """Prints bolded info message.

        Args:
            msg (str): Message

        """
        return self.info(f"\n{msg}", bold=True)

    def error(self, msg, exception=None, **kwargs):
        """Prints message with error formatting.

        :param msg:
        :param **kwargs:
        :return: method to print msg
        :rtype: method

        """
        bold = kwargs.pop("bold", (exception is not None))
        self.echo(
            msg,
            log="error",
            title_color="red",
            title_bold=True,
            fg="red",
            accent="red",
            bold=bold,
            **kwargs,
        )
        if exception:
            return self.exception(exception)

    def warn(self, msg, **kwargs):
        """Prints message with warn formatting.

        :param msg:
        :param **kwargs:
        :return: method to print msg
        :rtype: method

        """
        return self.echo(msg, log="warning", title_color="red", title_bold=True)

    def exception(self, error, **kwargs):
        """Prints message with exception formatting.

        :param error:
        :param **kwargs:
        :return: method to print msg
        :rtype: method

        """
        name = type(error).__name__
        msg = f"{name}: {str(error)}"
        return self.echo(msg, log="exception", title_color="red", fg="red", accent="red", **kwargs)

    def success(self, msg, **kwargs):
        """Prints message with success formatting.

        :param msg:
        :param **kwargs:
        :return: method to print msg
        :rtype: method
        :return: method to print msg
        :rtype: method

        """
        message = f"\u2714 {msg}"
        return self.echo(message, log="info", fg="green", **kwargs)

    def debug(self, msg, **kwargs):
        """Prints message with debug formatting.

        :param msg:
        :param **kwargs:
        :return: method to log msg
        :rtype: method

        """
        if self.stdout:
            with self.silent():
                return self.debug(msg, **kwargs)
        self.echo(msg, log="debug")
        return msg


================================================
FILE: micropy/main.py
================================================
"""Main Module."""

from __future__ import annotations

from pathlib import Path
from typing import List, Optional

import attr
from micropy import data, utils
from micropy.logger import Log
from micropy.project import Project, modules
from micropy.stubs import RepositoryInfo, StubManager, StubRepository
from pydantic import parse_file_as


@attr.define(kw_only=True)
class MicroPyOptions:
    root_dir: Path = attr.field(default=data.FILES)
    stubs_dir: Path = attr.Factory(lambda self: self.root_dir / "stubs", takes_self=True)


class MicroPy:
    """Handles App State Management."""

    RUN_CHECKS = True
    repo: StubRepository
    config: MicroPyOptions
    _stubs: Optional[StubManager] = None

    def __init__(self, *, options: Optional[MicroPyOptions] = None):
        self.config = options or MicroPyOptions()
        self.log = Log.get_logger("MicroPy")
        self.verbose = True
        self.log.debug("MicroPy Loaded")
        repo_list = parse_file_as(List[RepositoryInfo], data.REPO_SOURCES)
        self.repo = StubRepository()
        for repo_info in repo_list:
            self.repo = self.repo.add_repository(repo_info)
        if not self.config.stubs_dir.exists():
            self.setup()

    def setup(self):
        """Creates necessary directories for micropy."""
        self.log.debug("Running first time setup...")
        self.log.debug(f"Creating .micropy directory @ {self.config.root_dir}")
        self.config.stubs_dir.mkdir(parents=True, exist_ok=True)

    @property
    def stubs(self) -> StubManager:
        """Primary Stub Manager for MicroPy.

        Returns:
            StubManager: StubManager Instance

        """
        if not self._stubs:
            self._stubs = StubManager(resource=self.config.stubs_dir, repos=self.repo)
        return self._stubs

    @utils.lazy_property
    def project(self):
        """Current active project if available.

        Returns:
            Project: Instance of Current Project

        """
        proj = self.resolve_project(".", verbose=self.verbose)
        return proj

    def resolve_project(self, path, verbose=True):
        """Returns project from path if it exists.

        Args:
            path (str): Path to test
            verbose (bool): Log to stdout. Defaults to True.

        Returns:
            Project if it exists

        """
        path = Path(path).absolute()
        proj = Project(path)
        proj.add(modules.StubsModule, self.stubs)
        proj.add(modules.PackagesModule, "requirements.txt")
        proj.add(modules.DevPackagesModule, "dev-requirements.txt")
        proj.add(modules.TemplatesModule, run_checks=self.RUN_CHECKS)
        if proj.exists:
            if verbose:
                self.log.title("Loading Project")
            proj.load()
            if verbose:
                self.log.success("Ready!")
            return proj
        return proj


================================================
FILE: micropy/packages/__init__.py
================================================
"""Packages Module.

Allows user to address different dependency types (package, module,
path, pypi, etc.) through a single uniform api.

"""

from pathlib import Path
from typing import Any, Optional, Union

import requirements

from .package import Package
from .source_package import PackageDependencySource, VCSDependencySource
from .source_path import LocalDependencySource


def create_dependency_source(
    requirement: str, name: Optional[str] = None, **kwargs: Any
) -> Union[LocalDependencySource, PackageDependencySource, VCSDependencySource]:
    """Factory for creating a dependency source object.

    Args:
        requirement (str): Package name/path/constraints in string form.
        name (str, optional): Override package name.
            Defaults to None.

    Returns:
        Appropriate Dependency Source

    """
    req = next(requirements.parse(str(requirement)))
    if req.local_file:
        path = Path(req.path)
        name = name or path.name
        pkg = Package(name, req.specs, path=req.path)
        source = LocalDependencySource(pkg, path)
        return source
    pkg = Package(**req.__dict__)
    if pkg.vcs is not None or pkg.revision is not None:
        return VCSDependencySource(pkg, **kwargs)
    return PackageDependencySource(pkg, **kwargs)


__all__ = [
    "Package",
    "PackageDependencySource",
    "LocalDependencySource",
    "create_dependency_source",
]


================================================
FILE: micropy/packages/package.py
================================================
from pathlib import Path
from typing import List, Optional, Tuple

import requirements
from packaging.utils import canonicalize_name


class Package:
    def __init__(
        self,
        name: str,
        specs: List[Tuple[str, str]],
        path: Optional[Path] = None,
        uri: Optional[str] = None,
        vcs: Optional[str] = None,
        revision: Optional[str] = None,
        line: Optional[str] = None,
        **kwargs,
    ):
        """Generic Python Dependency.

        Args:
            name (str): Name of package
            specs (List[Tuple[str, str]]): Package constraints.
            path: path to package

        """
        self._name = name
        self._specs = specs
        self._path = path
        self._uri = uri
        self._vcs = vcs
        self._revision = revision
        self._line = line
        self.editable = self._path is not None

    @property
    def name(self) -> str:
        return canonicalize_name(self._name)

    @property
    def path(self) -> Optional[Path]:
        if not self._path:
            return None
        return Path(self._path)

    @property
    def full_name(self) -> str:
        if self._line and self._vcs:
            return self._line
        if self._path:
            return self.pretty_specs
        if not self._specs:
            return self.name
        return f"{self.name}{self.pretty_specs}"

    @property
    def uri(self) -> Optional[str]:
        if self._vcs and self._vcs in self._uri[:4]:
            # handle 'git+https' schemas
            return self._uri[4:]
        return self._uri

    @property
    def vcs(self) -> Optional[str]:
        return self._vcs

    @property
    def revision(self) -> Optional[str]:
        return self._revision

    @property
    def line(self) -> Optional[str]:
        return self._line

    @property
    def specs(self) -> List[Tuple[str, str]]:
        return self._specs

    @property
    def pretty_specs(self) -> str:
        if self.line and self.vcs:
            return self.line
        if self._path:
            return f"-e {self._path}"
        if not self.specs:
            return "*"
        _specs = ["".join(i for i in s) for s in self.specs]
        return "".join(_specs)

    @classmethod
    def from_text(cls, name: str, specs: str) -> "Package":
        """Create package from text.

        Args:
            name: name of package
            specs: package constraints

        Returns:
            Package instance

        """
        if "http" in specs:
            req = next(requirements.parse(specs))
            return cls(**req.__dict__)
        if "-e" in specs:
            req = next(requirements.parse(specs))
            return cls(name, req.specs, path=req.path)
        req_name = name
        if specs != "*":
            req_name = f"{name}{specs}"
        req = next(requirements.parse(req_name))
        return cls(req.name, req.specs)

    def __str__(self) -> str:
        return self.full_name


================================================
FILE: micropy/packages/source.py
================================================
from contextlib import AbstractContextManager, ExitStack, contextmanager
from pathlib import Path
from typing import List, Optional, Tuple

from boltons import fileutils
from micropy import utils
from micropy.logger import Log, ServiceLog

from .package import Package


class DependencySource(AbstractContextManager):
    """Base class for managing dependency sources.

    Args:
        package (Package): package the source points too.

    """

    _ignore_stubs: List[str] = ["setup.py", "__version__", "test_"]

    def __init__(self, package: Package):
        self.is_local = False
        self._package = package
        self.log: ServiceLog = Log.add_logger(repr(self))

    @property
    def package(self) -> Package:
        return self._package

    @contextmanager
    def handle_cleanup(self):
        with ExitStack() as stack:
            stack.push(self)
            yield
            # no errors, continue on
            stack.pop_all()

    def get_root(self, path: Path) -> Optional[Path]:
        """Determines package root if it has one.

        Args:
            path (Path): Path to check

        Returns:
            bool: True if is package

        """
        init = next(path.rglob("__init__.py"), None)
        if init:
            return init.parent
        return None

    def generate_stubs(self, path: Path) -> List[Tuple[Path, Path]]:
        """Generate Stub Files from a package.

        Args:
            path (Path): Path to package.

        Returns:
            List[Tuple[Path, Path]]: List of tuples containing
                 a path to the original file and stub, respectively.

        """
        py_files = fileutils.iter_find_files(str(path), patterns="*.py", ignored=self._ignore_stubs)
        stubs = [utils.generate_stub(f) for f in py_files]
        return stubs

    def __enter__(self):
        """Method to prepare source."""

    def __exit__(self, *args):
        return super().__exit__(*args)

    def __repr__(self):
        return f"<{self.__class__.__name__} {self.package}>"


================================================
FILE: micropy/packages/source_package.py
================================================
import shutil
from pathlib import Path
from tempfile import mkdtemp
from typing import Any, Callable, List, Optional, Tuple, Union

from git import Repo
from micropy import utils
from micropy.exceptions import RequirementNotFound

from .package import Package
from .source import DependencySource


class PackageDependencySource(DependencySource):
    """Dependency Source for pypi packages.

    Args:
        package (Package): Package source points too.
        format_desc: Callback to format progress bar description.
            Defaults to None.

    """

    repo: str = "https://pypi.org/pypi/{name}/json"

    def __init__(self, package: Package, format_desc: Optional[Callable[..., Any]] = None):
        super().__init__(package)
        try:
            utils.ensure_valid_url(self.repo_url)
        except Exception as e:
            raise RequirementNotFound(
                f"{self.repo_url} is not a valid url!", package=self.package
            ) from e
        else:
            self._meta: dict = utils.get_package_meta(str(self.package), self.repo_url)
            self.format_desc = format_desc or (lambda n: n)

    @property
    def repo_url(self) -> str:
        _url = self.repo.format(name=self.package.name)
        return _url

    @property
    def source_url(self) -> str:
        return self._meta.get("url", None)

    @property
    def file_name(self) -> str:
        return utils.get_url_filename(self.source_url)

    def fetch(self) -> bytes:
        """Fetch package contents into memory.

        Returns:
            bytes: Package archive contents.

        """
        self.log.debug(f"fetching package: {self.file_name}")
        desc = self.format_desc(self.file_name)
        content = utils.stream_download(self.source_url, desc=desc)
        return content

    def __enter__(self) -> Union[Path, List[Tuple[Path, Path]]]:
        """Prepare Pypi package for installation.

        Extracts the package into a temporary directory then
        generates stubs for type hinting.
        This helps with intellisense.

        If the dependency is a module, a list
        of tuples with the file and stub path, respectively,
        will be returned. Otherwise, the path to the package
        root will be returned.

        Returns:
            Root package path or list of files.

        """
        self.tmp_path = Path(mkdtemp())
        with self.handle_cleanup():
            path = utils.extract_tarbytes(self.fetch(), self.tmp_path)
            stubs = self.generate_stubs(path)
            pkg_root = self.get_root(path)
        return pkg_root or stubs

    def __exit__(self, *args):
        shutil.rmtree(self.tmp_path, ignore_errors=True)
        return super().__exit__(*args)


class VCSDependencySource(DependencySource):
    """Dependency Source for vcs packages."""

    def __init__(self, package: Package, format_desc: Optional[Callable[..., Any]] = None):
        super().__init__(package)
        self.log.debug(
            f"VCS package!, {self.package.revision}@{self.package.vcs}@{self.package.full_name}"
        )
        self._repo: Optional[Repo] = None
        try:
            utils.ensure_valid_url(self.repo_url)
        except Exception as e:
            raise RequirementNotFound(
                f"{self.repo_url} is not a valid VCS url!", package=self.package
            ) from e
        else:
            self.format_desc = format_desc or (lambda n: n)

    @property
    def repo_url(self) -> str:
        return self.package.uri

    @property
    def source_url(self) -> str:
        return self.package.uri

    @property
    def file_name(self) -> str:
        return self.package.name

    def fetch(self, dest_path: Path) -> Path:
        """Clones VCS repository to a given directory.

        Args:
            dest_path: Path to clone directory too.

        Returns:
            Path to clone repository.

        """
        self.log.debug(f"fetching vcs package: ${self.file_name} @ ${self.repo_url}")
        self.format_desc(self.file_name)
        self._repo = Repo.clone_from(self.repo_url, str(dest_path))
        return dest_path

    def __enter__(self) -> Union[Path, List[Tuple[Path, Path]]]:
        """Prepare VCS repository for installation.

        See PackageDependencySource.__enter__

        Returns:
            Root package path or list of files.

        """
        self.tmp_path = Path(mkdtemp())
        with self.handle_cleanup():
            path = self.fetch(self.tmp_path)
            stubs = self.generate_stubs(path)
            pkg_root = self.get_root(path)
        return pkg_root or stubs

    def __exit__(self, *args):
        shutil.rmtree(self.tmp_path, ignore_errors=True)
        return super().__exit__(*args)


================================================
FILE: micropy/packages/source_path.py
================================================
from pathlib import Path
from typing import List, Optional, Tuple, Union

from .package import Package
from .source import DependencySource


class LocalDependencySource(DependencySource):
    """Dependency Source that is available locally.

    Args:
        package (Package): Package source points too.
        path (Path): Path to package.

    """

    def __init__(self, package: Package, path: Path):
        super().__init__(package)
        self._path = path
        self.is_local = True

    @property
    def path(self) -> Path:
        return self._path

    def __enter__(self) -> Union[Path, List[Tuple[Path, Optional[Path]]]]:
        """Determines appropriate path.

        Returns:
            Path to package root or list of files.

        """
        return self.path


================================================
FILE: micropy/project/__init__.py
================================================
"""Module for generating/managing projects."""

from . import modules
from .project import Project

__all__ = ["Project", "modules"]


================================================
FILE: micropy/project/checks.py
================================================
"""Various requirement checks for templates."""

import subprocess as subproc
from functools import partial as _p

from micropy.logger import Log
from packaging import version

VSCODE_MS_PY_MINVER = "2019.9.34474"

log = Log.get_logger("MicroPy")


def iter_vscode_ext(name=None):
    """Iterates over installed VSCode Extensions.

    Args:
        name (str, optional): Name of Extension to Yield

    """
    _cmd = "code --list-extensions --show-versions"
    proc = subproc.run(_cmd, capture_output=True, shell=True)
    results = [e.strip() for e in proc.stdout.splitlines()]
    for ext in results:
        ename, vers = ext.split("@")
        if not name:
            yield (ename, vers)
        if name and ename == name:
            yield (ename, vers)


def vscode_ext_min_version(ext, min_version=VSCODE_MS_PY_MINVER, info=None):
    """Check if installed VScode Extension meets requirements.

    Args:
        ext (str): Name of Extension to Test
        min_version (str, optional): Minimum version.
            Defaults to VSCODE_MS_PY_MINVER.
        info (str, optional): Additional information to output.
            Defaults to None.

    Returns:
        bool: True if requirement is satisfied, False otherwise.

    """
    try:
        name, vers = next(iter_vscode_ext(name=ext), (ext, "0.0.0"))
    except Exception as e:
        log.debug(f"vscode check failed to run: {e}")
        log.debug("skipping...")
        return True
    else:
        cur_vers = version.parse(vers)
        min_vers = version.parse(min_version)
        if cur_vers >= min_vers:
            return True
        log.error(f"\nVSCode Extension {ext} failed to satisfy requirements!", bold=True)
        log.error(f"$[Min Required Version]: {min_vers}")
        log.error(f"$[Current Version:] {cur_vers}")
        if info:
            log.warn(info)
        return False


TEMPLATE_CHECKS = {
    "ms-python": _p(
        vscode_ext_min_version,
        "ms-python.python",
        info=("VSCode Integration will fail! " "See $[BradenM/micropy-cli#50] for details.\n"),
    ),
}


================================================
FILE: micropy/project/modules/__init__.py
================================================
"""Project Modules."""


from .modules import HookProxy, ProjectModule
from .packages import DevPackagesModule, PackagesModule
from .stubs import StubsModule
from .templates import TemplatesModule

__all__ = [
    "TemplatesModule",
    "PackagesModule",
    "StubsModule",
    "ProjectModule",
    "DevPackagesModule",
    "HookProxy",
]


================================================
FILE: micropy/project/modules/modules.py
================================================
from __future__ import annotations

import abc
import inspect
from copy import deepcopy
from functools import wraps
from typing import Any, Callable, List, Optional, Tuple, Type, TypeVar, Union

from micropy import utils
from micropy.config import Config
from micropy.logger import Log, ServiceLog

"""Project Packages Module Abstract Implementation"""

T = TypeVar("T")
ProxyItem = List[Tuple[T, str]]


class ProjectModule(metaclass=abc.ABCMeta):
    """Abstract Base Class for Project Modules."""

    PRIORITY: int = 0
    _hooks: List[HookProxy] = []

    def __init__(self, parent: Optional[ProjectModule] = None, log: Optional[ServiceLog] = None):
        self._parent = parent
        self.log = log

    @property
    def parent(self):
        """Component Parent."""
        return self._parent

    @parent.setter
    def parent(self, parent: Type[ProjectModule]) -> Type[ProjectModule]:
        """Sets component parent.

        Args:
            parent (Any): Parent to set

        """
        self._parent = parent
        return self.parent

    @abc.abstractproperty
    def config(self) -> Union[dict, Config]:
        """Config values specific to component."""

    @abc.abstractmethod
    def load(self):
        """Method to load component."""

    @abc.abstractmethod
    def create(self, *args: Any, **kwargs: Any) -> Any:
        """Method to create component."""

    @abc.abstractmethod
    def update(self):
        """Method to update component."""

    # FIXME: B027
    def add(self, component: Type[ProjectModule], *args: Any, **kwargs: Any) -> Any:  # noqa: B027
        """Adds component.

        Args:
            component (Any): Component to add.

        """

    # FIXME: B027
    def remove(self, component: Type[ProjectModule]) -> Any:  # noqa: B027
        """Removes component.

        Args:
            component (Any): Component to remove.

        """

    @classmethod
    def hook(cls, *args: Any, **kwargs: Any) -> Callable[..., Any]:
        """Decorator for creating a Project Hook.

        Allows decorated method to be called from parent
        container.

        Returns:
            Callable: Decorated function.

        """

        def _hook(func: T) -> Callable[..., Any]:
            name = kwargs.get("name", func.__name__)
            hook = next((i for i in cls._hooks if i._name == name), None)
            if not hook:
                hook = HookProxy(name)
                ProjectModule._hooks.append(hook)
            hook.add_method(func, **kwargs)

            @wraps(func)
            def wrapper(*args: Any, **kwargs: Any) -> T:
                return func(*args, **kwargs)

            return wrapper

        return _hook

    def resolve_hook(self, name: str) -> Union[Optional[HookProxy], T]:
        """Resolves appropriate hook for attribute name.

        Args:
            name (str): Attribute name to resolve hook for.

        Returns:
            Optional[HookProxy]: Callable Proxy for ProjectHook.
            NoneType: Name could not be resolved.

        """
        _hook = None
        for hook in self._hooks:
            if hook._name == name:
                _hook = hook
                _hook.add_instance(self)
                if _hook.is_descriptor():
                    return _hook.get()
        return _hook


class HookProxy:
    """Proxy for Project Hooks.

    Allows multiple project hooks with the same name by
    creating individual hooks for any defined permutations
    of kwargs.

    This is accomplished by creating a unique name for each
    permutation proxying the original attribute name to the
    appropriate method determined from the provided kwargs.

    Args:
        name (str): Name of Proxy

    """

    def __init__(self, name: str):
        self.methods: List[ProxyItem[Callable[..., Any]]] = []
        self.instances: List[Type[ProjectModule]] = []
        self._name: str = name
        self.log: ServiceLog = Log.add_logger(str(self))

    def __call__(self, *args, **kwargs):
        proxy_kwargs = deepcopy(kwargs)
        proxy = self.resolve_proxy(**proxy_kwargs)
        if proxy:
            return getattr(*proxy)(*args, **kwargs)

    def __str__(self):
        name = f"HookProxy({self._name})"
        return name

    def __repr__(self):
        name = f"HookProxy(name={self._name}, methods=[{self.methods}])"
        return name

    def resolve_proxy(self, **kwargs: Any) -> (Type[ProjectModule], str):
        """Resolves appropriate instance and method to proxy to.

        If additional kwargs are provided and a proxy is not found,
        the function will continue to remove one kwarg and
        recurse into itself until either a match is found or it runs
        out of kwargs.

        Returns:
            Instance and method name if resolved, otherwise None.

        """
        proxy_kwargs = deepcopy(kwargs)
        for method, name in self.methods:
            _name = self.get_name(method, proxy_kwargs)
            if name == _name:
                instance = self._get_instance(method)
                self.log.debug(f"{self._name} proxied to [{_name}@{instance}]")
                return (instance, method.__name__)
        if proxy_kwargs:
            self.log.debug(
                f"could not resolve proxy: {self._name}[{proxy_kwargs}], broadening search..."
            )
            proxy_kwargs.popitem()
            return self.resolve_proxy(**proxy_kwargs)
        return None

    def _get_instance(self, attr: Callable[..., Any]) -> Optional[Type[ProjectModule]]:
        """Retrieves instance from attribute.

        Args:
            attr (Callable): Attribute to use.

        Returns:
            Instance the attribute belongs to.

        """
        _class = utils.get_class_that_defined_method(attr)
        if _class:
            instance = next((i for i in self.instances if isinstance(i, _class)), None)
            return instance

    def is_descriptor(self) -> bool:
        """Determine if initial method provided is a descriptor."""
        method = self.methods[0][0]
        instance = self._get_instance(method)
        if instance:
            attr = inspect.getattr_static(instance, self._name)
            return inspect.isdatadescriptor(attr)
        return False

    def get(self) -> T:
        """Get initial method descriptor value."""
        instance = self._get_instance(self.methods[0][0])
        self.log.debug(f"{self._name} proxied to [property@{instance}]")
        return getattr(instance, self._name)

    def add_method(self, func: Callable[..., Any], **kwargs: Any) -> ProxyItem[Callable[..., Any]]:
        """Adds method to Proxy.

        Any kwargs provided will be used to generate the unique
        hook name.

        Args:
            func (Callable): Method to add

        Example:
            >>> def test_func(arg1, kwarg1=False):
                    pass

            >>> self.add_method(test_func, {'kwarg1': False})
            (test_func, '_hook__test_func__kwarg1_False')

        Returns:
            Tuple[Callable, str]: Tuple containing method and unique hook name.

        """
        name = self.get_name(func, kwargs)
        hook = (func, name)
        self.methods.append(hook)
        self.log.debug(f"Method added to proxy: {hook}")
        return hook  # type: ignore

    def add_instance(self, inst: Any) -> Any:
        """Add instance to Proxy.

        Args:
            inst (Any): Instance to add.

        """
        return self.instances.append(inst)

    def get_name(self, func: Callable[..., Any], params: Optional[dict] = None) -> str:
        """Generates name from method and provided kwargs.

        Args:
            func (Callable): Method to generate name for.
            params (Dict[Any, Any], optional): Any kwargs to update the defaults with.
                Defaults to None. If none, uses default kwargs.

        Returns:
            str: Generated name

        """
        params = params or {}
        sig = inspect.signature(func)
        _default = {
            p.name: p.default
            for p in sig.parameters.values()
            if p.kind == p.POSITIONAL_OR_KEYWORD and p.default is not p.empty
        }
        params = {**_default, **params}
        name = f"_hook__{self._name}__{'__'.join(f'{k}_{v}' for k, v in params.items())}"
        return name


================================================
FILE: micropy/project/modules/packages.py
================================================
"""Project Packages Module."""

import shutil
from pathlib import Path
from typing import Any, Optional, Union

from boltons import fileutils
from micropy import utils
from micropy.config import Config
from micropy.packages import (
    LocalDependencySource,
    Package,
    PackageDependencySource,
    create_dependency_source,
)
from micropy.project.modules import ProjectModule


class PackagesModule(ProjectModule):
    """Project Module for handling requirements.

    Args:
        path (str): Path to create requirements file at.
        packages (dict, optional): Initial packages to use.
            Defaults to None.

    """

    name: str = "packages"
    PRIORITY: int = 7

    def __init__(self, path, **kwargs):
        super().__init__(**kwargs)
        self._path = Path(path)

    @property
    def packages(self):
        _packages = self.config.get(self.name, {})
        return _packages

    @property
    def path(self):
        """Path to requirements file.

        Returns:
            Path: Path to file

        """
        path = self.parent.path / self._path
        return path

    @property
    def pkg_path(self):
        """Path to package data folder.

        Returns:
            Path: Path to folder.

        """
        return self.parent.data_path / self.parent.name

    @property
    def config(self) -> Config:
        """Config values specific to component.

        Returns:
            Component config.

        """
        return self.parent.config

    @property
    def context(self) -> Config:
        """Context values specific to component.

        Returns:
            Context values.

        """
        return self.parent.context

    @property
    def cache(self) -> Config:
        """Project Cache.

        Returns:
            Project wide cache

        """
        return self.parent.cache

    def install_package(self, source: Union[LocalDependencySource, PackageDependencySource]) -> Any:
        with source as files:
            if source.is_local:
                self.log.debug(f"installing {source} as local")
                return
            if isinstance(files, list):
                self.log.debug(f"installing {source} as module(s)")
                # Iterates over flattened list of stubs tuple
                file_paths = [(f, (self.pkg_path / f.name)) for f in list(sum(files, ()))]
                for paths in file_paths:
                    shutil.move(*paths)  # overwrites if existing
                return file_paths
            self.log.debug(f"installing {source} as package")
            pkg_path = self.pkg_path / source.package.name
            return fileutils.copytree(files, pkg_path)

    @ProjectModule.hook(dev=False)
    def add_from_file(self, path: Optional[Path] = None, dev: bool = False, **kwargs: Any) -> dict:
        """Loads all requirements from file.

        Args:
            path: Path to file. Defaults to self.path.
            dev: If dev requirements should be loaded.
                Defaults to False.

        """
        path = path or self.path
        reqs = utils.iter_requirements(path)
        self.log.debug(f"loading requirements from: {path}")
        for r in reqs:
            pkg = create_dependency_source(r.line).package
            if not self.packages.get(pkg.name):
                self.config.add(self.name + "/" + pkg.name, pkg.pretty_specs)
                if pkg.editable:
                    self.context.extend("local_paths", [pkg.path], unique=True)
        return self.packages

    @ProjectModule.hook()
    def add_package(self, package, dev=False, **kwargs):
        """Add requirement to project.

        Args:
            package (str): package name/spec
            dev (bool, optional): If dev requirements should be loaded.
                Defaults to False.

        Returns:
            dict: Dictionary of packages

        """
        self.log.debug(f"adding new dependency: {package}")
        source = create_dependency_source(package, **kwargs)
        pkg = source.package
        self.log.info(f"Adding $[{pkg.name}] to requirements...")
        if self.packages.get(pkg.name, None):
            self.log.error(f"$[{pkg}] is already installed!")
            self.update()
            return None
        self.config.add(self.name + "/" + pkg.name, pkg.pretty_specs)
        try:
            self.load()
        except Exception as e:
            self.log.error(f"Failed to install: {pkg.name}", exception=e)
            self.config.pop(self.name + "/" + pkg.name)
            raise
        else:
            if pkg.editable:
                self.context.extend("local_paths", [pkg.path], unique=True)
            self.log.success("Package installed!")
        finally:
            self.parent.update()
            # TODO: B012
            return self.packages  # noqa

    def load(self, fetch=True, **kwargs):
        """Retrieves and stubs project requirements."""
        self.pkg_path.mkdir(parents=True, exist_ok=True)
        if self.path.exists():
            self.add_from_file(self.path)
        pkg_keys = set(self.packages.keys())
        pkg_cache = self.cache.get(self.name)
        new_pkgs = pkg_keys.copy()
        if pkg_cache:
            new_pkgs = new_pkgs - set(pkg_cache)
        new_packages = [
            Package.from_text(name, spec)
            for name, spec in self.packages.items()
            if name in new_pkgs
        ]
        if fetch:
            if new_packages:
                self.log.title("Fetching Requirements")
            for req in new_packages:

                def format_desc(p):
                    return "".join(self.log.iter_formatted(f"$B[{p}]"))

                source = create_dependency_source(
                    str(req),
                    name=req.name,
                    format_desc=lambda p: f"{self.log.get_service()} {format_desc(p)}",
                )
                self.install_package(source)
        self.update()
        self.cache.upsert(self.name, list(pkg_keys))

    def create(self):
        """Create project files."""
        self.pkg_path.mkdir(parents=True, exist_ok=True)
        if not self.config.get(self.name):
            self.config.add(self.name, {})
        return self.update()

    def update(self):
        """Dumps packages to file at path."""
        if not self.path.exists():
            self.path.touch()
        pkgs = [Package.from_text(name, spec) for name, spec in self.config.get(self.name).items()]
        self.log.debug(f"dumping to {self.path.name}")
        with self.path.open("r+") as f:
            content = [c.strip() for c in f.readlines() if c.strip() != ""]
            _lines = sorted({str(p) for p in pkgs} | set(content))
            lines = [line + "\n" for line in _lines]
            self.log.debug(f"dumping: {lines}")
            f.seek(0)
            f.writelines(lines)
        local_paths = [p.path for p in pkgs if p.editable]
        if local_paths:
            self.context.add("local_paths", local_paths)
        self.context.extend("paths", [self.pkg_path], unique=True)


class DevPackagesModule(PackagesModule):
    """Project Module for Dev Packages."""

    PRIORITY: int = 8

    def __init__(self, path, **kwargs):
        super().__init__(path, **kwargs)
        self.name = "dev-packages"

    def create(self):
        """Creates component."""
        self.config.add(f"{self.name}/micropy-cli", "*")
        super().create()

    def load(self, *args, **kwargs):
        """Load component."""
        super().load(*args, **kwargs, fetch=False)

    @ProjectModule.hook(dev=True)
    def add_package(self, package, **kwargs):
        """Adds package."""
        return super().add_package(package, **kwargs)

    @ProjectModule.hook(dev=True)
    def add_from_file(self, path=None, **kwargs):
        """Adds packages from file."""
        return super().add_from_file(path=path, **kwargs)


================================================
FILE: micropy/project/modules/stubs.py
================================================
"""Project Stubs Module."""

import sys
from pathlib import Path
from typing import Any, List, Sequence, Union

from boltons import setutils
from micropy.project.modules import ProjectModule
from micropy.stubs import StubManager
from micropy.stubs.stubs import DeviceStub


class StubsModule(ProjectModule):
    """Project module for handling Stubs.

    Args:
        stub_manager (StubManager): StubManager instance.
        stubs (List[Type[Stub]], optional): Initial Stubs to use.

    """

    PRIORITY: int = 9

    def __init__(
        self, stub_manager: StubManager, stubs: Sequence[DeviceStub] = None, **kwargs: Any
    ):
        super().__init__(**kwargs)
        self.stub_manager: StubManager = stub_manager
        self._stubs: Sequence[DeviceStub] = stubs or []

    @property
    def context(self):
        """Component stub context."""
        return self.parent.context

    @property
    def config(self) -> dict:
        """Component specific config values.

        Returns:
            dict: Current config.

        """
        return self.parent.config

    @property
    @ProjectModule.hook()
    def stubs(self) -> Union[StubManager, Sequence[DeviceStub]]:
        """Component stubs.

        Returns:
            List[micropy.stubs.Stub]: List of stubs used in project.

        """
        _stubs = self.context.get("stubs", [])
        return self._resolve_subresource(_stubs)

    def get_stub_tree(self, stubs) -> Sequence[Path]:
        """Retrieve and order paths to base stubs and any stubs they depend on.

        Args:
            stubs: List of Stub Items

        Returns:
            Paths to all stubs project depends on.

        """
        stub_tree = setutils.IndexedSet()
        base_stubs = setutils.IndexedSet([s.stubs for s in stubs])
        frozen = [s.frozen for s in stubs]
        fware_mods = [s.firmware.frozen for s in stubs if s.firmware is not None]
        stub_tree.update(*frozen, *fware_mods, *base_stubs)
        return list(stub_tree)

    def _resolve_subresource(
        self, stubs: List[DeviceStub]
    ) -> Union[StubManager, Sequence[DeviceStub]]:
        """Resolves stub resource.

        Args:
            stubs (stubs): Stubs Passed to Manager

        """
        if not hasattr(self, "_parent"):
            return self._stubs
        if not self.parent.exists:
            return self._stubs
        try:
            resource = set(self.stub_manager.resolve_subresource(stubs, self.parent.data_path))
        except OSError as e:
            self.log.error("Failed to Create Stub Links!", exception=e)
            sys.exit(1)
        else:
            self.config.upsert("stubs", {s.name: s.stub_version for s in stubs})
            return resource

    def _load_stub_data(self, stub_data=None, **kwargs):
        """Loads Serialized Stub Data.

        Args:
            stub_data (dict): Dict of Stubs

        """
        for name, location in stub_data.items():
            _path = Path(location).absolute()
            if Path(_path).exists():
                yield self.stub_manager.add(_path)
            yield self.stub_manager.add(name)

    def load(self, **kwargs):
        """Loads stubs from info file.

        Args:
            stub_list (dict): Dict of Stubs

        """
        self.config.upsert("stubs", {s.name: s.stub_version for s in self._stubs})
        stubs = list(self._load_stub_data(stub_data=self.config.get("stubs")))
        stubs.extend(self.stubs)
        stubs = self._resolve_subresource(stubs)
        self.context.upsert("stubs", stubs)
        self.context.upsert("paths", self.get_stub_tree(self.stubs))
        return self.stubs

    def create(self):
        """Create stub project files."""
        self.log.info(f"Stubs: $[{' '.join(str(s) for s in self.stubs)}]")
        return self.load()

    def update(self):
        """Update current project stubs."""
        self.load()
        return self.stubs

    @ProjectModule.hook()
    def add_stub(self, stub, **kwargs):
        """Add stub to project.

        Args:
            stub (Stub): Stub object to add

        Returns:
            [Stubs]: Project Stubs

        """
        self.context.extend("stubs", [stub])
        self.log.info("Loading project...")
        self._resolve_subresource(self.stubs)
        self.log.info("Updating Project Info...")
        self.parent.update()
        self.log.info(f"Project Stubs: $[{' '.join(str(s) for s in self.stubs)}]")
        self.log.success("\nProject Updated!")
        return self.stubs


================================================
FILE: micropy/project/modules/templates.py
================================================
"""Project Templates Module."""


from micropy.project.modules import ProjectModule
from micropy.project.template import TemplateProvider


class TemplatesModule(ProjectModule):
    """Project Templates Module.

    Generates and manages project files using the Projects
    context.

    Args:
        templates (List[str]): List of templates to use.
        run_checks (bool, optional): Whether to execute checks or not.
            Defaults to True.

    """

    PRIORITY: int = 1
    TEMPLATES = TemplateProvider.TEMPLATES
    _dynamic = ["vscode", "pylint"]

    def __init__(self, templates=None, run_checks=True, **kwargs):
        self._templates = templates or []
        super().__init__(**kwargs)
        self.run_checks = run_checks

    @property
    def config(self):
        """Template config.

        Returns:
            dict: Current configuration

        """
        return self.parent.config

    def get_provider(self, templates):
        return TemplateProvider(templates, run_checks=self.run_checks, log=self.log)

    def load(self, **kwargs):
        """Loads project templates."""
        self.provider = self.get_provider(self.config.get("config"))
        templates = [k for k, v in self.config.get("config").items() if v]
        self.log.debug(f"Loading Templates: {templates}")
        self.provider = TemplateProvider(templates, **kwargs)
        self.update()

    def create(self):
        """Generates project files.

        Returns:
            dict: Project context

        """
        self.log.title("Rendering Templates")
        self.log.info("Populating Stub Info...")
        for key in self._templates:
            if key in self._dynamic:
                self.config.add("config" + "/" + key, True)
        self.provider = self.get_provider(self._templates)
        for t in self.provider.templates:
            self.provider.render_to(t, self.parent.path, **self.parent.context.raw())
        self.log.success("Stubs Injected!")
        return self._templates

    def update(self):
        """Updates project files.

        Returns:
            dict: Project context

        """
        self.provider = self.get_provider(self.config.get("config"))
        self.log.debug(f"updating templates with context: {self.parent.context.raw()}")
        for tmp in self.provider.templates:
            self.provider.update(tmp, self.parent.path, **self.parent.context.raw())
        return self.parent.context


================================================
FILE: micropy/project/project.py
================================================
"""Hosts functionality relating to generation of user projects."""

from pathlib import Path
from typing import Any, Iterator, List, Optional, Type

from boltons.queueutils import PriorityQueue
from micropy.config import Config, DictConfigSource
from micropy.logger import Log, ServiceLog
from micropy.project.modules import ProjectModule


class Project(ProjectModule):
    """Micropy Project.

    Args:
        path (str): Path to project root.
        name (str, optional): Name of Project.
            Defaults to None. If none, uses name of current directory.

    """

    def __init__(self, path: str, name: Optional[str] = None, **kwargs: Any):
        self._children: List[Type[ProjectModule]] = []
        self.path: Path = Path(path).absolute()
        self.data_path: Path = self.path / ".micropy"
        self.info_path: Path = self.path / "micropy.json"
        self.cache_path: Path = self.data_path / ".cache"
        self._cache = Config(self.cache_path)
        self._context = Config(source_format=DictConfigSource, default={"datadir": self.data_path})
        self.name: str = name or self.path.name
        default_config = {
            "name": self.name,
        }
        self._config: Config = Config(self.info_path, default=default_config)
        self.log: ServiceLog = Log.add_logger(self.name, show_title=False)

    def __getattr__(self, name: str) -> Any:
        results = iter([c.resolve_hook(name) for c in self._children])
        for res in results:
            if res is not None:
                self.log.debug(f"Hook Resolved: {name} -> {res}")
                return res
        return self.__getattribute__(name)

    @property
    def exists(self) -> bool:
        """Whether this project exists.

        Returns:
            bool: True if it exists

        """
        return self.info_path.exists()

    @property
    def config(self) -> Config:
        """Project Configuration.

        Returns:
            Config: Project Config Instance

        """
        return self._config

    @property
    def context(self) -> Config:
        """Project context used in templates.

        Returns:
            Config: Current context

        """
        return self._context

    @property
    def cache(self) -> Config:
        """Project wide cache.

        Returns:
            Cache instance

        """
        return self._cache

    def iter_children_by_priority(self) -> Iterator[Type[ProjectModule]]:
        """Iterate project modules by priority.

        Yields:
            the next child item

        """
        pq = PriorityQueue()
        for i in self._children:
            pq.add(i, i.PRIORITY)
        more = pq.peek(default=False)
        while more:
            yield pq.pop()
            more = pq.peek(default=False)

    def add(self, component, *args, **kwargs):
        """Adds project component.

        Args:
            component (Any): Component to add.

        """
        child = component(*args, **kwargs, log=self.log, parent=self)
        self._children.append(child)
        self.log.debug(f"adding module: {type(child).__name__}")

    def remove(self, component):
        """Removes project component.

        Args:
            component (Any): Component to remove.

        """
        child = next(i for i in self._children if isinstance(i, component))
        self._children.remove(child)

    def load(self, **kwargs: Any) -> "Project":
        """Loads all components in Project.

        Returns:
            Current Project Instance

        """
        self.name = self._config.get("name")
        self.data_path.mkdir(exist_ok=True)
        for child in self.iter_children_by_priority():
            child.load(**kwargs)
        return self

    def create(self):
        """Creates new Project.

        Returns:
            Path: Path relative to current active directory.

        """
        self.log.title(f"Initiating $[{self.name
Download .txt
gitextract_7ldyguvi/

├── .chglog/
│   ├── CHANGELOG.tpl.md
│   └── config.yml
├── .editorconfig
├── .git-blame-ignore-revs
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   ├── config.yml
│   │   ├── feature_request.md
│   │   └── question.md
│   ├── actions/
│   │   └── setup-micropy/
│   │       └── action.yml
│   ├── codeql/
│   │   └── codeql-config.yml
│   ├── renovate.json5
│   └── workflows/
│       ├── changelog.yml
│       ├── codeql-analysis.yml
│       ├── main.yml
│       ├── publish.yml
│       └── release.yml
├── .gitignore
├── .pre-commit-config.yaml
├── .prettierignore
├── .readthedocs.yml
├── .release-please-manifest.json
├── .tool-versions
├── CHANGELOG.md
├── LICENSE
├── Makefile
├── README.md
├── docs/
│   ├── Makefile
│   ├── _autosummary/
│   │   ├── micropy.config.config_source.rst
│   │   ├── micropy.config.rst
│   │   ├── micropy.exceptions.rst
│   │   ├── micropy.main.rst
│   │   ├── micropy.packages.rst
│   │   ├── micropy.project.modules.rst
│   │   ├── micropy.project.rst
│   │   ├── micropy.rst
│   │   ├── micropy.stubs.rst
│   │   ├── micropy.stubs.source.rst
│   │   └── micropy.utils.rst
│   ├── base.md
│   ├── cli.rst
│   ├── conf.py
│   ├── header.rst
│   ├── index.rst
│   └── modules.rst
├── micropy/
│   ├── __init__.py
│   ├── __main__.py
│   ├── app/
│   │   ├── __init__.py
│   │   ├── main.py
│   │   └── stubs.py
│   ├── config/
│   │   ├── __init__.py
│   │   ├── config.py
│   │   ├── config_dict.py
│   │   ├── config_json.py
│   │   └── config_source.py
│   ├── data/
│   │   ├── __init__.py
│   │   ├── schemas/
│   │   │   ├── firmware.json
│   │   │   └── stubs.json
│   │   └── sources.json
│   ├── exceptions.py
│   ├── logger.py
│   ├── main.py
│   ├── packages/
│   │   ├── __init__.py
│   │   ├── package.py
│   │   ├── source.py
│   │   ├── source_package.py
│   │   └── source_path.py
│   ├── project/
│   │   ├── __init__.py
│   │   ├── checks.py
│   │   ├── modules/
│   │   │   ├── __init__.py
│   │   │   ├── modules.py
│   │   │   ├── packages.py
│   │   │   ├── stubs.py
│   │   │   └── templates.py
│   │   ├── project.py
│   │   ├── template/
│   │   │   ├── .gitignore
│   │   │   ├── .pylintrc
│   │   │   ├── .vscode/
│   │   │   │   ├── extensions.json
│   │   │   │   └── settings.json
│   │   │   ├── pymakr.conf
│   │   │   └── src/
│   │   │       ├── boot.py
│   │   │       └── main.py
│   │   └── template.py
│   ├── py.typed
│   ├── pyd/
│   │   ├── __init__.py
│   │   ├── abc.py
│   │   ├── backend_rshell.py
│   │   ├── backend_upydevice.py
│   │   ├── consumers.py
│   │   └── pydevice.py
│   ├── stubs/
│   │   ├── __init__.py
│   │   ├── manifest.py
│   │   ├── package.py
│   │   ├── repo.py
│   │   ├── repo_package.py
│   │   ├── repositories/
│   │   │   ├── __init__.py
│   │   │   ├── micropy.py
│   │   │   └── micropython.py
│   │   ├── repository_info.py
│   │   ├── source.py
│   │   └── stubs.py
│   └── utils/
│       ├── __init__.py
│       ├── _compat.py
│       ├── decorators.py
│       ├── helpers.py
│       ├── stub.py
│       ├── types.py
│       └── validate.py
├── pyproject.toml
├── release-please-config.json
├── scripts/
│   └── export-docs-reqs.sh
└── tests/
    ├── __init__.py
    ├── app/
    │   ├── conftest.py
    │   ├── test_main.py
    │   └── test_stubs.py
    ├── conftest.py
    ├── data/
    │   ├── esp32_test_stub/
    │   │   ├── frozen/
    │   │   │   ├── ntptime.py
    │   │   │   └── ntptime.pyi
    │   │   ├── info.json
    │   │   └── stubs/
    │   │       ├── machine.py
    │   │       └── modules.json
    │   ├── esp8266_invalid_stub/
    │   │   └── info.json
    │   ├── esp8266_test_stub/
    │   │   ├── frozen/
    │   │   │   ├── ntptime.py
    │   │   │   └── ntptime.pyi
    │   │   ├── info.json
    │   │   └── stubs/
    │   │       ├── machine.py
    │   │       └── modules.json
    │   ├── fware_test_stub/
    │   │   ├── frozen/
    │   │   │   ├── utarfile.py
    │   │   │   ├── utarfile.pyi
    │   │   │   ├── utokenize.py
    │   │   │   └── utokenize.pyi
    │   │   └── info.json
    │   ├── project_test/
    │   │   ├── .pylintrc
    │   │   ├── .vscode/
    │   │   │   └── settings.json
    │   │   └── micropy.json
    │   ├── stubber_test_stub/
    │   │   ├── micropython.py
    │   │   └── modules.json
    │   ├── test_repo.json
    │   ├── test_source.xml
    │   └── test_sources.json
    ├── test_checks.py
    ├── test_config.py
    ├── test_highlevel.py
    ├── test_main.py
    ├── test_packages.py
    ├── test_project.py
    ├── test_pyd.py
    ├── test_stub_source.py
    ├── test_stubs/
    │   ├── bad_test_stub/
    │   │   └── modules.json
    │   ├── esp32_test_stub/
    │   │   ├── frozen/
    │   │   │   ├── ntptime.py
    │   │   │   └── ntptime.pyi
    │   │   ├── info.json
    │   │   └── stubs/
    │   │       ├── machine.py
    │   │       └── modules.json
    │   └── esp8266_test_stub/
    │       ├── frozen/
    │       │   ├── ntptime.py
    │       │   └── ntptime.pyi
    │       ├── info.json
    │       └── stubs/
    │           ├── machine.py
    │           └── modules.json
    ├── test_stubs.py
    ├── test_stubs_repo.py
    ├── test_template.py
    ├── test_utils/
    │   ├── fail.json
    │   ├── pass.json
    │   └── schema.json
    └── test_utils.py
Download .txt
SYMBOL INDEX (766 symbols across 74 files)

FILE: docs/conf.py
  function setup (line 87) | def setup(app):

FILE: micropy/app/main.py
  function main_callback (line 24) | def main_callback(ctx: typer.Context):
  function main_version (line 54) | def main_version():
  function template_callback (line 66) | def template_callback(
  function path_callback (line 86) | def path_callback(ctx: typer.Context, value: Optional[Path]) -> Optional...
  function name_callback (line 92) | def name_callback(ctx: typer.Context, value: Optional[str]) -> Optional[...
  function stubs_callback (line 105) | def stubs_callback(ctx: typer.Context, value: Optional[List[str]]) -> Op...
  function main_init (line 134) | def main_init(
  function ensure_project (line 197) | def ensure_project(ctx: typer.Context) -> Project:
  function install_local_callback (line 207) | def install_local_callback(ctx: typer.Context, value: Optional[Path]) ->...
  function install_project_callback (line 222) | def install_project_callback(ctx: typer.Context, value: Optional[List[st...
  function main_install (line 245) | def main_install(

FILE: micropy/app/stubs.py
  function stubs_callback (line 32) | def stubs_callback():
  class CreateBackend (line 54) | class CreateBackend(str, Enum):
    method __new__ (line 58) | def __new__(cls, value: str, backend: Type[MetaPyDeviceBackend]):
  function create_changeset (line 65) | def create_changeset(
  function stubs_create (line 74) | def stubs_create(
  function stubs_add (line 206) | def stubs_add(ctx: typer.Context, stub_name: str, force: bool = False):
  function stubs_search (line 248) | def stubs_search(ctx: typer.Context, query: str, show_outdated: bool = F...
  function stubs_list (line 273) | def stubs_list(ctx: typer.Context):

FILE: micropy/config/config.py
  class Config (line 14) | class Config:
    method __init__ (line 29) | def __init__(
    method source (line 46) | def source(self) -> ConfigSource:
    method source (line 50) | def source(self, value: Any) -> ConfigSource:
    method config (line 55) | def config(self) -> dict:
    method raw (line 58) | def raw(self) -> dict:
    method sync (line 61) | def sync(self) -> dict:
    method parse_key (line 72) | def parse_key(self, key: str) -> Tuple[Sequence[str], str]:
    method get (line 94) | def get(self, key: str, default: Any = None) -> Any:
    method set (line 114) | def set(self, key: str, value: Any) -> Any:
    method add (line 129) | def add(self, key: str, value: Any) -> Any:
    method pop (line 144) | def pop(self, key: str) -> Any:
    method extend (line 163) | def extend(self, key: str, value: List[Any], unique: bool = False) -> ...
    method upsert (line 182) | def upsert(self, key: str, value: Union[List[Any], dict]) -> dict:
    method search (line 198) | def search(self, key):

FILE: micropy/config/config_dict.py
  class DictConfigSource (line 6) | class DictConfigSource(ConfigSource):
    method __init__ (line 7) | def __init__(self, config: Optional[dict] = None):
    method exists (line 18) | def exists(self) -> bool:
    method process (line 21) | def process(self) -> dict:
    method prepare (line 24) | def prepare(self):
    method save (line 27) | def save(self, content: dict) -> dict:

FILE: micropy/config/config_json.py
  class JSONConfigSource (line 9) | class JSONConfigSource(ConfigSource):
    method __init__ (line 17) | def __init__(self, path: Path):
    method file_path (line 22) | def file_path(self) -> Path:
    method file_path (line 27) | def file_path(self, value: Path) -> Path:
    method exists (line 41) | def exists(self) -> bool:
    method process (line 44) | def process(self) -> dict:
    method prepare (line 57) | def prepare(self):
    method save (line 63) | def save(self, content: dict) -> Path:

FILE: micropy/config/config_source.py
  class ConfigSource (line 10) | class ConfigSource(contextlib.AbstractContextManager, metaclass=abc.ABCM...
    method __init__ (line 19) | def __init__(self, initial_config: Optional[dict] = None):
    method config (line 24) | def config(self) -> dict:
    method config (line 29) | def config(self, value: dict) -> dict:
    method exists (line 43) | def exists(self) -> bool:
    method save (line 47) | def save(self, content: Any) -> Any:
    method process (line 51) | def process(self) -> dict:
    method prepare (line 60) | def prepare(self) -> Any:
    method __enter__ (line 63) | def __enter__(self) -> dict:
    method __exit__ (line 68) | def __exit__(self, *args):

FILE: micropy/exceptions.py
  class MicropyException (line 5) | class MicropyException(Exception):
  class StubError (line 9) | class StubError(MicropyException):
    method __init__ (line 12) | def __init__(self, message=None, stub=None):
  class StubValidationError (line 20) | class StubValidationError(StubError):
    method __init__ (line 23) | def __init__(self, path, errors, *args, **kwargs):
    method __str__ (line 27) | def __str__(self):
  class StubNotFound (line 31) | class StubNotFound(StubError):
    method __init__ (line 34) | def __init__(self, stub_name=None):
  class RequirementException (line 40) | class RequirementException(MicropyException):
    method __init__ (line 43) | def __init__(self, *args, **kwargs):
  class RequirementNotFound (line 48) | class RequirementNotFound(RequirementException):
  class PyDeviceError (line 52) | class PyDeviceError(MicropyException):
    method __init__ (line 55) | def __init__(self, message: str = None):
  class PyDeviceConnectionError (line 60) | class PyDeviceConnectionError(PyDeviceError):
    method __init__ (line 63) | def __init__(self, location: str):
  class PyDeviceFileIntegrityError (line 67) | class PyDeviceFileIntegrityError(PyDeviceError):
    method __init__ (line 72) | def __init__(self, device_path: str, device_sum: str, digest: str):

FILE: micropy/logger.py
  class Log (line 16) | class Log:
    method __init__ (line 21) | def __init__(self):
    method add_logger (line 27) | def add_logger(cls, service_name, base_color="white", **kwargs):
    method get_logger (line 36) | def get_logger(cls, service_name):
  class ServiceLog (line 43) | class ServiceLog:
    method __init__ (line 55) | def __init__(self, service_name="MicroPy", base_color="bright_green", ...
    method silent (line 68) | def silent(self):
    method load_handler (line 73) | def load_handler(self):
    method parse_msg (line 92) | def parse_msg(self, msg, accent_color=None):
    method get_parents (line 118) | def get_parents(self, names=None):
    method get_service (line 128) | def get_service(self, **kwargs):
    method iter_formatted (line 145) | def iter_formatted(self, message, **kwargs):
    method echo (line 159) | def echo(self, msg, **kwargs):
    method info (line 193) | def info(self, msg, **kwargs):
    method title (line 204) | def title(self, msg, **kwargs):
    method error (line 213) | def error(self, msg, exception=None, **kwargs):
    method warn (line 236) | def warn(self, msg, **kwargs):
    method exception (line 247) | def exception(self, error, **kwargs):
    method success (line 260) | def success(self, msg, **kwargs):
    method debug (line 274) | def debug(self, msg, **kwargs):

FILE: micropy/main.py
  class MicroPyOptions (line 17) | class MicroPyOptions:
  class MicroPy (line 22) | class MicroPy:
    method __init__ (line 30) | def __init__(self, *, options: Optional[MicroPyOptions] = None):
    method setup (line 42) | def setup(self):
    method stubs (line 49) | def stubs(self) -> StubManager:
    method project (line 61) | def project(self):
    method resolve_project (line 71) | def resolve_project(self, path, verbose=True):

FILE: micropy/packages/__init__.py
  function create_dependency_source (line 18) | def create_dependency_source(

FILE: micropy/packages/package.py
  class Package (line 8) | class Package:
    method __init__ (line 9) | def __init__(
    method name (line 38) | def name(self) -> str:
    method path (line 42) | def path(self) -> Optional[Path]:
    method full_name (line 48) | def full_name(self) -> str:
    method uri (line 58) | def uri(self) -> Optional[str]:
    method vcs (line 65) | def vcs(self) -> Optional[str]:
    method revision (line 69) | def revision(self) -> Optional[str]:
    method line (line 73) | def line(self) -> Optional[str]:
    method specs (line 77) | def specs(self) -> List[Tuple[str, str]]:
    method pretty_specs (line 81) | def pretty_specs(self) -> str:
    method from_text (line 92) | def from_text(cls, name: str, specs: str) -> "Package":
    method __str__ (line 115) | def __str__(self) -> str:

FILE: micropy/packages/source.py
  class DependencySource (line 12) | class DependencySource(AbstractContextManager):
    method __init__ (line 22) | def __init__(self, package: Package):
    method package (line 28) | def package(self) -> Package:
    method handle_cleanup (line 32) | def handle_cleanup(self):
    method get_root (line 39) | def get_root(self, path: Path) -> Optional[Path]:
    method generate_stubs (line 54) | def generate_stubs(self, path: Path) -> List[Tuple[Path, Path]]:
    method __enter__ (line 69) | def __enter__(self):
    method __exit__ (line 72) | def __exit__(self, *args):
    method __repr__ (line 75) | def __repr__(self):

FILE: micropy/packages/source_package.py
  class PackageDependencySource (line 14) | class PackageDependencySource(DependencySource):
    method __init__ (line 26) | def __init__(self, package: Package, format_desc: Optional[Callable[.....
    method repo_url (line 39) | def repo_url(self) -> str:
    method source_url (line 44) | def source_url(self) -> str:
    method file_name (line 48) | def file_name(self) -> str:
    method fetch (line 51) | def fetch(self) -> bytes:
    method __enter__ (line 63) | def __enter__(self) -> Union[Path, List[Tuple[Path, Path]]]:
    method __exit__ (line 86) | def __exit__(self, *args):
  class VCSDependencySource (line 91) | class VCSDependencySource(DependencySource):
    method __init__ (line 94) | def __init__(self, package: Package, format_desc: Optional[Callable[.....
    method repo_url (line 110) | def repo_url(self) -> str:
    method source_url (line 114) | def source_url(self) -> str:
    method file_name (line 118) | def file_name(self) -> str:
    method fetch (line 121) | def fetch(self, dest_path: Path) -> Path:
    method __enter__ (line 136) | def __enter__(self) -> Union[Path, List[Tuple[Path, Path]]]:
    method __exit__ (line 152) | def __exit__(self, *args):

FILE: micropy/packages/source_path.py
  class LocalDependencySource (line 8) | class LocalDependencySource(DependencySource):
    method __init__ (line 17) | def __init__(self, package: Package, path: Path):
    method path (line 23) | def path(self) -> Path:
    method __enter__ (line 26) | def __enter__(self) -> Union[Path, List[Tuple[Path, Optional[Path]]]]:

FILE: micropy/project/checks.py
  function iter_vscode_ext (line 14) | def iter_vscode_ext(name=None):
  function vscode_ext_min_version (line 32) | def vscode_ext_min_version(ext, min_version=VSCODE_MS_PY_MINVER, info=No...

FILE: micropy/project/modules/modules.py
  class ProjectModule (line 19) | class ProjectModule(metaclass=abc.ABCMeta):
    method __init__ (line 25) | def __init__(self, parent: Optional[ProjectModule] = None, log: Option...
    method parent (line 30) | def parent(self):
    method parent (line 35) | def parent(self, parent: Type[ProjectModule]) -> Type[ProjectModule]:
    method config (line 46) | def config(self) -> Union[dict, Config]:
    method load (line 50) | def load(self):
    method create (line 54) | def create(self, *args: Any, **kwargs: Any) -> Any:
    method update (line 58) | def update(self):
    method add (line 62) | def add(self, component: Type[ProjectModule], *args: Any, **kwargs: An...
    method remove (line 71) | def remove(self, component: Type[ProjectModule]) -> Any:  # noqa: B027
    method hook (line 80) | def hook(cls, *args: Any, **kwargs: Any) -> Callable[..., Any]:
    method resolve_hook (line 107) | def resolve_hook(self, name: str) -> Union[Optional[HookProxy], T]:
  class HookProxy (line 128) | class HookProxy:
    method __init__ (line 144) | def __init__(self, name: str):
    method __call__ (line 150) | def __call__(self, *args, **kwargs):
    method __str__ (line 156) | def __str__(self):
    method __repr__ (line 160) | def __repr__(self):
    method resolve_proxy (line 164) | def resolve_proxy(self, **kwargs: Any) -> (Type[ProjectModule], str):
    method _get_instance (line 191) | def _get_instance(self, attr: Callable[..., Any]) -> Optional[Type[Pro...
    method is_descriptor (line 206) | def is_descriptor(self) -> bool:
    method get (line 215) | def get(self) -> T:
    method add_method (line 221) | def add_method(self, func: Callable[..., Any], **kwargs: Any) -> Proxy...
    method add_instance (line 247) | def add_instance(self, inst: Any) -> Any:
    method get_name (line 256) | def get_name(self, func: Callable[..., Any], params: Optional[dict] = ...

FILE: micropy/project/modules/packages.py
  class PackagesModule (line 19) | class PackagesModule(ProjectModule):
    method __init__ (line 32) | def __init__(self, path, **kwargs):
    method packages (line 37) | def packages(self):
    method path (line 42) | def path(self):
    method pkg_path (line 53) | def pkg_path(self):
    method config (line 63) | def config(self) -> Config:
    method context (line 73) | def context(self) -> Config:
    method cache (line 83) | def cache(self) -> Config:
    method install_package (line 92) | def install_package(self, source: Union[LocalDependencySource, Package...
    method add_from_file (line 109) | def add_from_file(self, path: Optional[Path] = None, dev: bool = False...
    method add_package (line 130) | def add_package(self, package, dev=False, **kwargs):
    method load (line 166) | def load(self, fetch=True, **kwargs):
    method create (line 198) | def create(self):
    method update (line 205) | def update(self):
  class DevPackagesModule (line 224) | class DevPackagesModule(PackagesModule):
    method __init__ (line 229) | def __init__(self, path, **kwargs):
    method create (line 233) | def create(self):
    method load (line 238) | def load(self, *args, **kwargs):
    method add_package (line 243) | def add_package(self, package, **kwargs):
    method add_from_file (line 248) | def add_from_file(self, path=None, **kwargs):

FILE: micropy/project/modules/stubs.py
  class StubsModule (line 13) | class StubsModule(ProjectModule):
    method __init__ (line 24) | def __init__(
    method context (line 32) | def context(self):
    method config (line 37) | def config(self) -> dict:
    method stubs (line 48) | def stubs(self) -> Union[StubManager, Sequence[DeviceStub]]:
    method get_stub_tree (line 58) | def get_stub_tree(self, stubs) -> Sequence[Path]:
    method _resolve_subresource (line 75) | def _resolve_subresource(
    method _load_stub_data (line 97) | def _load_stub_data(self, stub_data=None, **kwargs):
    method load (line 110) | def load(self, **kwargs):
    method create (line 125) | def create(self):
    method update (line 130) | def update(self):
    method add_stub (line 136) | def add_stub(self, stub, **kwargs):

FILE: micropy/project/modules/templates.py
  class TemplatesModule (line 8) | class TemplatesModule(ProjectModule):
    method __init__ (line 25) | def __init__(self, templates=None, run_checks=True, **kwargs):
    method config (line 31) | def config(self):
    method get_provider (line 40) | def get_provider(self, templates):
    method load (line 43) | def load(self, **kwargs):
    method create (line 51) | def create(self):
    method update (line 69) | def update(self):

FILE: micropy/project/project.py
  class Project (line 12) | class Project(ProjectModule):
    method __init__ (line 22) | def __init__(self, path: str, name: Optional[str] = None, **kwargs: Any):
    method __getattr__ (line 37) | def __getattr__(self, name: str) -> Any:
    method exists (line 46) | def exists(self) -> bool:
    method config (line 56) | def config(self) -> Config:
    method context (line 66) | def context(self) -> Config:
    method cache (line 76) | def cache(self) -> Config:
    method iter_children_by_priority (line 85) | def iter_children_by_priority(self) -> Iterator[Type[ProjectModule]]:
    method add (line 100) | def add(self, component, *args, **kwargs):
    method remove (line 111) | def remove(self, component):
    method load (line 121) | def load(self, **kwargs: Any) -> "Project":
    method create (line 134) | def create(self):
    method update (line 153) | def update(self):

FILE: micropy/project/template.py
  class Template (line 13) | class Template:
    method __init__ (line 27) | def __init__(self, template, **kwargs):
    method context (line 35) | def context(self):
    method iter_clean (line 39) | def iter_clean(self, data=None):
    method run_checks (line 53) | def run_checks(self):
    method update (line 65) | def update(self, root):
    method update_as_json (line 85) | def update_as_json(self, path):
    method update_as_text (line 98) | def update_as_text(self, path, by_contains=None):
    method render_stream (line 122) | def render_stream(self):
    method iter_relative_paths (line 127) | def iter_relative_paths(self, paths: List[Path], strict: bool = False)...
    method __str__ (line 151) | def __str__(self):
  class GenericTemplate (line 156) | class GenericTemplate(Template):
    method __init__ (line 159) | def __init__(self, *args, **kwargs):
    method context (line 164) | def context(self):
  class CodeTemplate (line 169) | class CodeTemplate(Template):
    method __init__ (line 177) | def __init__(self, *args, **kwargs):
    method context (line 184) | def context(self):
  class PylintTemplate (line 201) | class PylintTemplate(Template):
    method __init__ (line 206) | def __init__(self, *args, **kwargs):
    method context (line 212) | def context(self):
  class TemplateProvider (line 223) | class TemplateProvider:
    method __init__ (line 248) | def __init__(self, templates, log=None, **kwargs):
    method get (line 271) | def get(self, name, *args, **kwargs):
    method render_to (line 290) | def render_to(self, name, parent_dir, *args, **kwargs):
    method update (line 317) | def update(self, name, root_dir, **kwargs):
    method templates (line 339) | def templates(self):

FILE: micropy/pyd/abc.py
  class StartHandler (line 12) | class StartHandler(Protocol):
    method __call__ (line 13) | def __call__(self, *, name: str = None, size: int | None = None) -> Any:
  class UpdateHandler (line 17) | class UpdateHandler(Protocol):
    method __call__ (line 18) | def __call__(self, *, size: int | None = None) -> Any:
  class EndHandler (line 22) | class EndHandler(Protocol):
    method __call__ (line 23) | def __call__(self) -> Any:
  class MessageHandler (line 27) | class MessageHandler(Protocol):
    method __call__ (line 28) | def __call__(self, data: AnyStr) -> Any:
  class StreamConsumer (line 32) | class StreamConsumer(Protocol):
    method on_start (line 35) | def on_start(self) -> StartHandler:
    method on_update (line 40) | def on_update(self) -> UpdateHandler:
    method on_end (line 45) | def on_end(self) -> EndHandler:
  class MessageConsumer (line 49) | class MessageConsumer(Protocol):
    method on_message (line 52) | def on_message(self) -> MessageHandler:
  class PyDeviceConsumer (line 56) | class PyDeviceConsumer(MessageConsumer, StreamConsumer, Protocol):
  class MetaPyDeviceBackend (line 60) | class MetaPyDeviceBackend(abc.ABC):
    method establish (line 64) | def establish(self, target: str) -> MetaPyDeviceBackend:
    method connect (line 68) | def connect(self) -> None:
    method disconnect (line 72) | def disconnect(self) -> None:
    method reset (line 76) | def reset(self) -> None:
    method resolve_path (line 80) | def resolve_path(self, target_path: DevicePath | str | Path) -> Device...
    method connected (line 85) | def connected(self) -> bool:
    method push_file (line 89) | def push_file(
    method pull_file (line 100) | def pull_file(
    method list_dir (line 111) | def list_dir(self, path: DevicePath) -> list[DevicePath]:
    method remove (line 115) | def remove(self, path: DevicePath) -> None:
    method copy_dir (line 119) | def copy_dir(
    method eval (line 130) | def eval(self, command: str, *, consumer: MessageConsumer | None = None):
    method eval_script (line 134) | def eval_script(
  class MetaPyDevice (line 147) | class MetaPyDevice(abc.ABC, Generic[AnyBackend]):
    method connect (line 153) | def connect(self) -> None:
    method disconnect (line 157) | def disconnect(self) -> None:
    method copy_to (line 161) | def copy_to(self, source_path: HostPath, target_path: DevicePath) -> N...
    method copy_from (line 165) | def copy_from(
    method remove (line 171) | def remove(self, target_path: DevicePath) -> None:
    method run_script (line 175) | def run_script(

FILE: micropy/pyd/backend_rshell.py
  class RShell (line 21) | class RShell:
    method connect (line 25) | def connect(self, port: str):
  class RShellConsumer (line 43) | class RShellConsumer:
    method __init__ (line 46) | def __init__(self, child_consumer: MessageHandler):
    method _output (line 50) | def _output(self, data: str):
    method on_message (line 63) | def on_message(self, char: bytes):
  class RShellPyDeviceBackend (line 84) | class RShellPyDeviceBackend(MetaPyDeviceBackend):
    method _pyb_root (line 94) | def _pyb_root(self) -> str:
    method connected (line 102) | def connected(self) -> bool:
    method resolve_path (line 105) | def resolve_path(self, path: str | DevicePath | Path) -> DevicePath:
    method establish (line 112) | def establish(self, target: str) -> RShellPyDeviceBackend:
    method connect (line 119) | def connect(self) -> None:
    method disconnect (line 130) | def disconnect(self) -> None:
    method reset (line 133) | def reset(self) -> None:
    method push_file (line 136) | def push_file(self, source_path: HostPath, target_path: DevicePath = N...
    method pull_file (line 153) | def pull_file(self, source_path: DevicePath, target_path: HostPath, **...
    method list_dir (line 158) | def list_dir(self, path: DevicePath) -> list[DevicePath]:
    method copy_dir (line 169) | def copy_dir(
    method repl (line 195) | def repl(self):
    method eval (line 208) | def eval(self, command: str, *, consumer: MessageConsumer = None):
    method eval_script (line 216) | def eval_script(
    method remove (line 234) | def remove(self, path: DevicePath) -> None:

FILE: micropy/pyd/backend_upydevice.py
  class UOS (line 32) | class UOS(UPY_UOS):
    method stat (line 34) | def stat(self, path):
  function retry (line 38) | def retry(fn: Callable[P, T]) -> Callable[P, T | None]:
  class UPyDeviceBackend (line 69) | class UPyDeviceBackend(MetaPyDeviceBackend):
    method _ensure_connected (line 75) | def _ensure_connected(self):
    method _rand_device_path (line 79) | def _rand_device_path(self) -> DevicePath:
    method uos (line 84) | def uos(self) -> UOS:
    method _pyb_root (line 90) | def _pyb_root(self) -> DevicePath:
    method resolve_path (line 96) | def resolve_path(self, path: DevicePath | str | Path) -> DevicePath:
    method establish (line 105) | def establish(self, target: str) -> UPyDeviceBackend:
    method connect (line 110) | def connect(self):
    method disconnect (line 116) | def disconnect(self):
    method reset (line 120) | def reset(self):
    method connected (line 127) | def connected(self) -> bool:
    method list_dir (line 130) | def list_dir(self, path: DevicePath) -> list[DevicePath]:
    method iter_files (line 133) | def iter_files(self, path: DevicePath) -> Generator[DevicePath, None, ...
    method copy_dir (line 147) | def copy_dir(
    method push_file (line 172) | def push_file(
    method pull_file (line 179) | def pull_file(self, source_path: DevicePath, target_path: HostPath, **...
    method _iter_hex_chunks (line 188) | def _iter_hex_chunks(self, content: str):
    method write_file (line 195) | def write_file(
    method _compute_chunk_size (line 229) | def _compute_chunk_size(self) -> int:
    method _compute_device_file_digest (line 235) | def _compute_device_file_digest(
    method read_file (line 260) | def read_file(
    method eval (line 314) | def eval(self, command: str, *, consumer: MessageConsumer = NoOpConsum...
    method eval_script (line 319) | def eval_script(
    method remove (line 333) | def remove(self, path: DevicePath) -> None:

FILE: micropy/pyd/consumers.py
  class ProgressStreamConsumer (line 17) | class ProgressStreamConsumer:
    method __init__ (line 20) | def __init__(
    method on_start (line 29) | def on_start(self, *, name: str = None, size: int | None = None):
    method on_update (line 41) | def on_update(self, *, size: int | None = None):
    method on_end (line 44) | def on_end(self):
  class ConsumerDelegate (line 48) | class ConsumerDelegate:
    method __init__ (line 51) | def __init__(self, *consumers: StreamConsumer | MessageConsumer | None):
    method consumer_for (line 54) | def consumer_for(self, action: str, *args, **kwargs):
  class StreamHandlers (line 67) | class StreamHandlers(NamedTuple):
  class MessageHandlers (line 73) | class MessageHandlers(NamedTuple):
  function _no_op (line 77) | def _no_op(*args, **kwargs):

FILE: micropy/pyd/pydevice.py
  class PyDevice (line 20) | class PyDevice(MetaPyDevice[AnyBackend], Generic[AnyBackend]):
    method __init__ (line 24) | def __init__(
    method copy_from (line 39) | def copy_from(
    method copy_to (line 64) | def copy_to(self, source_path: HostPath, target_path: DevicePath, **kw...
    method remove (line 71) | def remove(self, target_path: DevicePath) -> None:
    method connect (line 74) | def connect(self):
    method disconnect (line 77) | def disconnect(self):
    method run_script (line 80) | def run_script(
    method run (line 96) | def run(self, content: str) -> str | None:

FILE: micropy/stubs/manifest.py
  class StubsManifest (line 11) | class StubsManifest(GenericModel, Generic[AnyStubPackage], abc.ABC):
    class Config (line 12) | class Config:
    method resolve_package_url (line 19) | def resolve_package_url(self, package: StubPackage) -> str:
    method resolve_package_absolute_name (line 22) | def resolve_package_absolute_name(self, package: StubPackage) -> str:
    method resolve_package_versioned_name (line 26) | def resolve_package_versioned_name(self, package: StubPackage) -> str:
    method resolve_package_absolute_versioned_name (line 30) | def resolve_package_absolute_versioned_name(self, package: StubPackage...

FILE: micropy/stubs/package.py
  class StubPackage (line 8) | class StubPackage(BaseModel):
    class Config (line 9) | class Config:
    method package_name (line 17) | def package_name(self) -> str:

FILE: micropy/stubs/repo.py
  class StubRepository (line 19) | class StubRepository:
    method __attrs_post_init__ (line 31) | def __attrs_post_init__(self) -> None:
    method packages (line 41) | def packages(self) -> Iterator[StubRepositoryPackage]:
    method build_indexes (line 45) | def build_indexes(self) -> None:
    method add_repository (line 61) | def add_repository(self, info: RepositoryInfo) -> StubRepository:
    method search (line 90) | def search(
    method latest_for_package (line 111) | def latest_for_package(
    method resolve_package (line 119) | def resolve_package(self, name: str) -> StubRepositoryPackage:

FILE: micropy/stubs/repo_package.py
  class StubRepositoryPackage (line 10) | class StubRepositoryPackage:
    method url (line 15) | def url(self) -> str:
    method repo_name (line 19) | def repo_name(self) -> str:
    method name (line 23) | def name(self) -> str:
    method version (line 27) | def version(self) -> str:
    method absolute_name (line 31) | def absolute_name(self) -> str:
    method versioned_name (line 35) | def versioned_name(self) -> str:
    method absolute_versioned_name (line 39) | def absolute_versioned_name(self) -> str:
    method exact_matchers (line 43) | def exact_matchers(self) -> Iterator[str]:
    method partial_matchers (line 49) | def partial_matchers(self) -> Iterator[str]:
    method match_exact (line 54) | def match_exact(self, in_name: str) -> bool:

FILE: micropy/stubs/repositories/micropy.py
  class MicropyStubPackage (line 13) | class MicropyStubPackage(StubPackage):
  class MicropyStubsManifest (line 18) | class MicropyStubsManifest(StubsManifest[MicropyStubPackage]):
    method check (line 23) | def check(cls, values: dict):
    method resolve_package_url (line 31) | def resolve_package_url(self, package: StubPackage) -> str:

FILE: micropy/stubs/repositories/micropython.py
  class MicropythonStubsPackage (line 19) | class MicropythonStubsPackage(StubPackage):
    method package_version (line 24) | def package_version(self) -> NormalizedVersion:
    method __lt__ (line 27) | def __lt__(self, other: MicropythonStubsPackage) -> bool:
    method __eq__ (line 30) | def __eq__(self, other: MicropythonStubsPackage) -> bool:
  class MicropythonStubsManifest (line 34) | class MicropythonStubsManifest(StubsManifest[MicropythonStubsPackage]):
    method _get_packages (line 36) | def _get_packages(cls, v: dict[str, dict]):
    method resolve_package_url (line 40) | def resolve_package_url(self, package: StubPackage) -> str:

FILE: micropy/stubs/repository_info.py
  class RepositoryInfo (line 11) | class RepositoryInfo(BaseModel):
    class Config (line 16) | class Config:
    method fetch_source (line 20) | def fetch_source(self) -> dict[str, Any]:

FILE: micropy/stubs/source.py
  class LocateStrategy (line 29) | class LocateStrategy(Protocol):
    method prepare (line 31) | def prepare(self, location: PathStr) -> Union[PathStr, tuple[PathStr, ...
  class StubSource (line 39) | class StubSource:
    method _default_locators (line 46) | def _default_locators(self: StubSource) -> list[LocateStrategy]:
    method _do_locate (line 49) | def _do_locate(self, stack: ExitStack, path: PathStr, locator: LocateS...
    method ready (line 62) | def ready(self, location: Optional[PathStr] = None) -> ContextManager[...
  class StubInfoSpecLocator (line 81) | class StubInfoSpecLocator(LocateStrategy):
    method prepare (line 82) | def prepare(self, location: PathStr) -> PathStr:
  class RemoteStubLocator (line 88) | class RemoteStubLocator(LocateStrategy):
    method _unpack_archive (line 91) | def _unpack_archive(self, file_bytes: bytes, path: PathStr) -> PathStr:
    method prepare (line 107) | def prepare(self, location: PathStr) -> tuple[PathStr, Optional[Callab...
  class RepoStubLocator (line 129) | class RepoStubLocator(LocateStrategy):
    method prepare (line 132) | def prepare(self, location: PathStr) -> Union[PathStr, tuple[PathStr, ...
  function get_source (line 144) | def get_source(location, **kwargs):

FILE: micropy/stubs/stubs.py
  class StubManager (line 19) | class StubManager:
    method __init__ (line 40) | def __init__(self, resource=None, repos=None):
    method __iter__ (line 49) | def __iter__(self):
    method __len__ (line 52) | def __len__(self):
    method iter_by_firmware (line 55) | def iter_by_firmware(self, stubs=None):
    method verbose_log (line 70) | def verbose_log(self, state):
    method _load (line 83) | def _load(self, stub_source, strict=True, **kwargs):
    method resolve_firmware (line 127) | def resolve_firmware(self, stub):
    method validate (line 155) | def validate(self, path, schema=None):
    method _get_stubtype (line 182) | def _get_stubtype(self, path):
    method is_valid (line 206) | def is_valid(self, path):
    method _check_existing (line 223) | def _check_existing(self, location):
    method load_from (line 253) | def load_from(self, directory, *args, **kwargs):
    method _should_recurse (line 276) | def _should_recurse(self, location):
    method add (line 299) | def add(self, location, dest=None, force=False):
    method from_stubber (line 338) | def from_stubber(self, path, dest):
    method from_metadata (line 373) | def from_metadata(self, package_name: str, path: Path) -> dict[str, str]:
    method resolve_subresource (line 422) | def resolve_subresource(self, stubs, subresource):
  class Stub (line 444) | class Stub:
    method __init__ (line 458) | def __init__(self, path, copy_to=None, **kwargs):
    method find_root (line 465) | def find_root(self, path: Path) -> Path:
    method copy_to (line 472) | def copy_to(self, dest, name=None):
    method resolve_link (line 481) | def resolve_link(cls, stub, link_path):
    method name (line 499) | def name(self):
    method __eq__ (line 503) | def __eq__(self, other):
    method __hash__ (line 506) | def __hash__(self):
    method __str__ (line 509) | def __str__(self):
  class DeviceStub (line 513) | class DeviceStub(Stub):
    method __init__ (line 525) | def __init__(self, path, copy_to=None, **kwargs):
    method firmware_name (line 545) | def firmware_name(self):
    method name (line 561) | def name(self):
    method __repr__ (line 568) | def __repr__(self):
  class FirmwareStub (line 576) | class FirmwareStub(Stub):
    method __init__ (line 588) | def __init__(self, path, copy_to=None, **kwargs):
    method name (line 597) | def name(self):
    method __repr__ (line 600) | def __repr__(self):

FILE: micropy/utils/decorators.py
  function lazy_property (line 12) | def lazy_property(fn):

FILE: micropy/utils/helpers.py
  function is_url (line 54) | def is_url(url):
  function ensure_valid_url (line 72) | def ensure_valid_url(url):
  function ensure_existing_dir (line 94) | def ensure_existing_dir(path: PathStr) -> Path:
  function is_existing_dir (line 123) | def is_existing_dir(path):
  function is_downloadable (line 141) | def is_downloadable(url):
  function get_url_filename (line 170) | def get_url_filename(url):
  function stream_download (line 185) | def stream_download(url, **kwargs):
  function search_xml (line 215) | def search_xml(url, node):
  function iter_requirements (line 236) | def iter_requirements(path):
  function get_package_meta (line 248) | def get_package_meta(name, url):
  function is_within_directory (line 283) | def is_within_directory(
  function safe_extract (line 294) | def safe_extract(
  function extract_tarbytes (line 309) | def extract_tarbytes(file_bytes: bytes, path: str) -> str:
  function create_dir_link (line 326) | def create_dir_link(source, target):
  function is_dir_link (line 367) | def is_dir_link(path):
  function is_update_available (line 389) | def is_update_available():
  function get_cached_data (line 408) | def get_cached_data(url):
  function get_class_that_defined_method (line 414) | def get_class_that_defined_method(meth):

FILE: micropy/utils/stub.py
  function locate_create_stubs (line 19) | def locate_create_stubs() -> Path:
  function import_source_code (line 24) | def import_source_code(module_name: str, path: Path) -> ModuleType:
  function import_stubber (line 42) | def import_stubber() -> ModuleType:
  function generate_stub (line 57) | def generate_stub(path, log_func=None):
  function prepare_create_stubs (line 85) | def prepare_create_stubs(

FILE: micropy/utils/types.py
  class SupportsLessThan (line 15) | class SupportsLessThan(Protocol):
    method __lt__ (line 16) | def __lt__(self, other: Any) -> bool:

FILE: micropy/utils/validate.py
  class Validator (line 15) | class Validator:
    method __init__ (line 23) | def __init__(self, schema_path):
    method _load_json (line 27) | def _load_json(self, path):
    method validate (line 41) | def validate(self, path):

FILE: tests/app/conftest.py
  function runner (line 13) | def runner():
  class MicroPyScenario (line 19) | class MicroPyScenario(NamedTuple):
  function mock_repo (line 26) | def mock_repo(mocker: MockFixture) -> StubRepository:
  function micropy_obj (line 33) | def micropy_obj(
  function context_mock (line 52) | def context_mock(request: pytest.FixtureRequest, mocker: MockFixture, mi...

FILE: tests/app/test_main.py
  function project_path (line 18) | def project_path(tmp_path):
  class TemplateParamCase (line 22) | class TemplateParamCase(NamedTuple):
  function template_param (line 36) | def template_param(request: pytest.FixtureRequest, mocker: MockFixture):
  function test_template_callback (line 58) | def test_template_callback(mocker: MockFixture, micropy_obj, context_moc...
  function test_path_callback (line 73) | def test_path_callback(mocker: MockFixture, micropy_obj, context_mock, i...
  function test_name_callback (line 89) | def test_name_callback(mocker: MockFixture, micropy_obj, context_mock, i...
  function test_main_callback (line 111) | def test_main_callback(mocker: MockFixture, context_mock, micropy_obj, e...
  function test_stubs_callback (line 134) | def test_stubs_callback(mocker: MockFixture, context_mock, input, prompt...
  function test_main_init (line 153) | def test_main_init(mocker, micropy_obj, project_path, runner):
  function test_ensure_project (line 177) | def test_ensure_project(mocker: MockFixture, micropy_obj, expect, tmp_pa...
  function test_install_local_callback (line 192) | def test_install_local_callback(micropy_obj, context_mock, input, expect...
  function test_install_project_callback (line 209) | def test_install_project_callback(mocker, context_mock, micropy_obj, pac...
  function test_main_install (line 234) | def test_main_install(mocker, micropy_obj, runner, dev_flag):
  function test_main_version (line 250) | def test_main_version(runner):

FILE: tests/app/test_stubs.py
  function test_create_changeset (line 19) | def test_create_changeset(input, expected):
  function pydevice_mock (line 29) | def pydevice_mock(mocker: MockerFixture):
  function pyb_mock (line 40) | def pyb_mock(request: pytest.FixtureRequest, mocker: MockerFixture):
  function stubs_locator_mock (line 48) | def stubs_locator_mock(mocker: MockerFixture):
  function stub_search_data (line 55) | def stub_search_data(mocker: MockerFixture):
  function test_stubs_create (line 72) | def test_stubs_create(mocker: MockerFixture, pyb_mock, micropy_obj, runn...
  function test_stubs_create__connect_error (line 79) | def test_stubs_create__connect_error(pydevice_mock, micropy_obj, runner):
  function test_stubs_create__script_error (line 85) | def test_stubs_create__script_error(pyb_mock, micropy_obj, runner):
  function test_stubs_add_success (line 96) | def test_stubs_add_success(micropy_obj, runner, stubs_locator_mock, mock...
  function test_stubs_add__not_found (line 110) | def test_stubs_add__not_found(micropy_obj, runner, stubs_locator_mock, m...
  function test_stubs_add__invalid (line 119) | def test_stubs_add__invalid(micropy_obj, runner, stubs_locator_mock, moc...
  function test_stubs_list (line 130) | def test_stubs_list(micropy_obj, runner):
  function test_stubs_search (line 142) | def test_stubs_search(stub_search_data, micropy_obj, runner, outdated):
  function test_stubs_search_no_results (line 160) | def test_stubs_search_no_results(mocker: MockerFixture, micropy_obj, run...

FILE: tests/conftest.py
  function pytest_collection_modifyitems (line 21) | def pytest_collection_modifyitems(items):
  function cleanup_data (line 26) | def cleanup_data(mocker):
  function mock_prompt (line 35) | def mock_prompt(monkeypatch):
  function mock_micropy_path (line 50) | def mock_micropy_path(mocker, tmp_path):
  function mock_micropy (line 61) | def mock_micropy(mock_micropy_path):
  function mock_cwd (line 68) | def mock_cwd(request, tmp_path, mocker):
  function test_urls (line 78) | def test_urls():
  function get_stub_paths (line 97) | def get_stub_paths(shared_datadir, tmp_path):
  function mock_mp_stubs (line 120) | def mock_mp_stubs(mock_micropy, mocker, shared_datadir):
  function get_stubs (line 128) | def get_stubs(get_stub_paths, mocker, tmp_path):
  function micropy_stubs (line 149) | def micropy_stubs(mocker, get_stubs):
  function test_archive (line 165) | def test_archive(shared_datadir):
  function mock_manifests (line 188) | def mock_manifests(mocker, requests_mock):
  function mock_checks (line 246) | def mock_checks(mocker):
  function mock_pkg (line 254) | def mock_pkg(mocker, tmp_path):
  function pytest_runtest_makereport (line 273) | def pytest_runtest_makereport(item, call):
  function pytest_runtest_setup (line 280) | def pytest_runtest_setup(item):
  class AssertUtils (line 287) | class AssertUtils:
    method dict_match_mocks (line 290) | def dict_match_mocks(self, d1):
    method dict_equal (line 299) | def dict_equal(self, d1, d2):
    method list_equal (line 309) | def list_equal(self, l1, l2):
    method load_json (line 314) | def load_json(self, path):
    method json_equal_dict (line 319) | def json_equal_dict(self, path, d2):
    method str_path (line 323) | def str_path(self, path, absolute=False):
    method get_rand_str (line 330) | def get_rand_str(self, length=10):
  function utils (line 339) | def utils():

FILE: tests/data/esp32_test_stub/frozen/ntptime.py
  function time (line 16) | def time():
  function settime (line 31) | def settime():

FILE: tests/data/esp32_test_stub/frozen/ntptime.pyi
  function time (line 7) | def time() -> Any: ...
  function settime (line 11) | def settime() -> None: ...

FILE: tests/data/esp32_test_stub/stubs/machine.py
  class ADC (line 8) | class ADC:
    method read (line 11) | def read():
  class I2C (line 20) | class I2C:

FILE: tests/data/esp8266_test_stub/frozen/ntptime.py
  function time (line 16) | def time():
  function settime (line 31) | def settime():

FILE: tests/data/esp8266_test_stub/frozen/ntptime.pyi
  function time (line 7) | def time() -> Any: ...
  function settime (line 11) | def settime() -> None: ...

FILE: tests/data/esp8266_test_stub/stubs/machine.py
  class ADC (line 8) | class ADC:
    method read (line 11) | def read():
  class I2C (line 20) | class I2C:

FILE: tests/data/fware_test_stub/frozen/utarfile.py
  function roundup (line 13) | def roundup(val, align):
  class FileSection (line 17) | class FileSection:
    method __init__ (line 18) | def __init__(self, f, content_len, aligned_len):
    method read (line 23) | def read(self, sz=65536):
    method readinto (line 33) | def readinto(self, buf):
    method skip (line 42) | def skip(self):
  class TarInfo (line 46) | class TarInfo:
    method __str__ (line 47) | def __str__(self):
  class TarFile (line 51) | class TarFile:
    method __init__ (line 52) | def __init__(self, name=None, fileobj=None):
    method next (line 59) | def next(self):
    method __iter__ (line 79) | def __iter__(self):
    method __next__ (line 82) | def __next__(self):
    method extractfile (line 88) | def extractfile(self, tarinfo):

FILE: tests/data/fware_test_stub/frozen/utarfile.pyi
  function roundup (line 7) | def roundup(val: Any, align: Any) -> Any: ...
  class FileSection (line 11) | class FileSection:
    method __init__ (line 12) | def __init__(self, f: Any, content_len: smallint, aligned_len: smallin...
    method read (line 13) | def read(self, sz: Any = 65536) -> Union[Any, bytes]: ...
    method readinto (line 18) | def readinto(self, buf: Any) -> Union[Any, number]: ...
    method skip (line 23) | def skip(self) -> None: ...
  class TarInfo (line 25) | class TarInfo:
    method __str__ (line 26) | def __str__(self) -> str: ...
  class TarFile (line 28) | class TarFile:
    method __init__ (line 29) | def __init__(self, name: str = None, fileobj: Any = None) -> None: ...
    method next (line 30) | def next(self) -> Optional[Any]: ...
    method __iter__ (line 37) | def __iter__(self) -> Any: ...
    method __next__ (line 40) | def __next__(self) -> Any: ...
    method extractfile (line 43) | def extractfile(self, tarinfo: Any) -> Any: ...

FILE: tests/data/fware_test_stub/frozen/utokenize.py
  class TokenInfo (line 12) | class TokenInfo(namedtuple("TokenInfo", ("type", "string", "start", "end...
    method __str__ (line 13) | def __str__(self):
  function get_indent (line 22) | def get_indent(l):
  function tokenize (line 28) | def tokenize(readline):

FILE: tests/data/fware_test_stub/frozen/utokenize.pyi
  class TokenInfo (line 7) | class TokenInfo(namedtuple(str, Tuple[str, str, str, str, str])):
    method __str__ (line 8) | def __str__(self) -> str: ...
  function get_indent (line 10) | def get_indent(l: Any) -> Tuple[int, Any]: ...
  function tokenize (line 11) | def tokenize(readline: Any) -> None: ...

FILE: tests/data/stubber_test_stub/micropython.py
  function alloc_emergency_exception_buf (line 8) | def alloc_emergency_exception_buf():
  function const (line 12) | def const():
  function heap_lock (line 16) | def heap_lock():
  function heap_unlock (line 20) | def heap_unlock():
  function kbd_intr (line 24) | def kbd_intr():
  function mem_info (line 28) | def mem_info():
  function opt_level (line 32) | def opt_level():
  function qstr_info (line 36) | def qstr_info():
  function schedule (line 40) | def schedule():
  function stack_use (line 44) | def stack_use():

FILE: tests/test_checks.py
  function test_vscode_ext_min_version (line 6) | def test_vscode_ext_min_version(mock_checks, mocker):

FILE: tests/test_config.py
  class TestConfig (line 7) | class TestConfig:
    method test_config (line 11) | def test_config(self, tmp_path):
    method get_file_data (line 16) | def get_file_data(self, conf):
    method test_default (line 20) | def test_default(self, tmp_path):
    method test_load_from_file (line 30) | def test_load_from_file(self, tmp_path, utils):
    method test_override (line 39) | def test_override(self, test_config, tmp_path):
    method test_get (line 49) | def test_get(self, test_config):
    method test_set (line 56) | def test_set(self, test_config):
    method test_update_from_file (line 63) | def test_update_from_file(self, test_config):
    method test_extend (line 75) | def test_extend(self, test_config):
    method test_upsert (line 83) | def test_upsert(self, test_config):
    method test_dict (line 90) | def test_dict(self):

FILE: tests/test_highlevel.py
  function mock_requests (line 11) | def mock_requests(mocker, requests_mock, test_archive):
  class TestCreateProject (line 37) | class TestCreateProject:
    method build_project (line 59) | def build_project(self, mpy, path):
    method check_mp_data (line 71) | def check_mp_data(self, path, utils, name="esp32", expect=None):
    method check_vscode (line 78) | def check_vscode(self, path, name="esp32", expect=None):
    method test_setup_stubs (line 88) | def test_setup_stubs(self, mock_micropy, get_stub_paths, shared_datadir):
    method test_create_project (line 93) | def test_create_project(self, micropy_stubs, tmp_path, utils):
    method test_add_package (line 99) | def test_add_package(self, mock_pkg, micropy_stubs, tmp_path, utils):
    method test_add_local_package (line 108) | def test_add_local_package(self, tmp_path, local_pkg, micropy_stubs, u...

FILE: tests/test_main.py
  function test_setup (line 6) | def test_setup(mock_micropy_path):
  function test_add_stub (line 19) | def test_add_stub(mock_micropy, shared_datadir):
  function test_stub_error (line 32) | def test_stub_error():
  function test_resolve_project (line 37) | def test_resolve_project(mocker, mock_micropy):

FILE: tests/test_packages.py
  class TestPackages (line 10) | class TestPackages:
    class MockSource (line 11) | class MockSource:
      method __init__ (line 12) | def __init__(self, pkg, has_init):
    method mock_source (line 17) | def mock_source(self, request, mock_pkg):
    method mock_source_path (line 24) | def mock_source_path(self, request, tmp_path):
    method test_factory (line 45) | def test_factory(self, package, expect):
    method test_package (line 70) | def test_package(self, mock_pkg, requirement, expect):
    method test_package_source (line 77) | def test_package_source(self, mock_source):
    method test_package_path (line 94) | def test_package_path(self, mock_source_path):
    method test_package_from_text (line 116) | def test_package_from_text(self, pkg, expect, utils):

FILE: tests/test_project.py
  function get_module (line 12) | def get_module(tmp_path):
  function get_config (line 36) | def get_config():
  function get_context (line 62) | def get_context():
  function test_project (line 95) | def test_project(micropy_stubs, tmp_path, get_module):
  function tmp_project (line 109) | def tmp_project(tmp_path, shared_datadir):
  function test_implementation (line 116) | def test_implementation(mocker):
  function test_project_queue (line 128) | def test_project_queue(tmp_path, mock_cwd, mocker):
  class TestProject (line 158) | class TestProject:
    method test_create (line 159) | def test_create(self, test_project, mock_checks, mods, utils):
    method test_config (line 169) | def test_config(self, test_project, get_config, mods, utils):
    method test_context (line 179) | def test_context(self, test_project, get_context, mods, tmp_path, utils):
    method test_load (line 191) | def test_load(self, mock_pkg, tmp_project, mock_checks, test_project, ...
    method test_update (line 195) | def test_update(self, mock_pkg, tmp_project, mock_checks, test_project...
  class TestStubsModule (line 200) | class TestStubsModule:
    method stub_module (line 202) | def stub_module(self, mocker, tmp_path, micropy_stubs):
    method test_load (line 212) | def test_load(self, tmp_project, stub_module, get_stub_paths):
    method test_add_stub (line 224) | def test_add_stub(self, test_project, get_stub_paths, mocker):
  class TestPackagesModule (line 238) | class TestPackagesModule:
    method test_package (line 240) | def test_package(self, mocker, tmp_path, mock_pkg, test_project):
    method test_add_package (line 247) | def test_add_package(self, test_project, mock_pkg, tmp_path):
    method test_add_local_package (line 256) | def test_add_local_package(self, test_project, tmp_path, utils):
    method test_package_error (line 263) | def test_package_error(self, test_project, mock_pkg, mocker, tmp_path,...
    method test_add_dev_package (line 273) | def test_add_dev_package(self, mocker, mock_pkg, test_project):

FILE: tests/test_pyd.py
  function mock_upy (line 19) | def mock_upy(mocker: MockFixture):
  function mock_upy_uos (line 25) | def mock_upy_uos(mocker: MockFixture):
  function mock_upy_retry (line 31) | def mock_upy_retry(mocker: MockFixture):
  function mock_rsh (line 37) | def mock_rsh(mocker: MockFixture):
  class MockAdapter (line 45) | class MockAdapter:
    method __init__ (line 50) | def __init__(self, backend: Literal["upy", "rsh"], mock: MagicMock, mo...
    method is_rsh (line 57) | def is_rsh(self) -> bool:
    method is_upy (line 61) | def is_upy(self) -> bool:
    method connect (line 65) | def connect(self) -> MagicMock:
    method device (line 69) | def device(self):
  function with_consumer (line 79) | def with_consumer(request: pytest.FixtureRequest, mocker: MockFixture):
  class TestPyDeviceBackend (line 86) | class TestPyDeviceBackend:
    method pymock_setup (line 102) | def pymock_setup(self, request: pytest.FixtureRequest):
    method pymock (line 111) | def pymock(self, pymock_setup, request: pytest.FixtureRequest, mock_up...
    method test_init (line 117) | def test_init(self, pymock):
    method test_init__connect_fail (line 127) | def test_init__connect_fail(self, pymock):
    method test_connected__default (line 133) | def test_connected__default(self, pymock):
    method test_connected (line 141) | def test_connected(self, pymock):
    method test_disconnect (line 146) | def test_disconnect(self, pymock):
    method test_reset (line 153) | def test_reset(self, pymock, mocker: MockFixture):
    method test_eval (line 162) | def test_eval(self, pymock, mocker: MockFixture, with_consumer):
    method test_eval_script (line 177) | def test_eval_script(self, pymock, mocker: MockFixture, with_consumer):
    method read_file_effects (line 194) | def read_file_effects(self):
    method test_read_file (line 207) | def test_read_file(self, mock_upy_retry, pymock):
    method test_read_file__with_integrity (line 222) | def test_read_file__with_integrity(self, mock_upy_retry, pymock):
    method test_read_file__with_integrity_fail (line 241) | def test_read_file__with_integrity_fail(self, mock_upy_retry, pymock, ...
    method test_read_file__bad_chunk (line 258) | def test_read_file__bad_chunk(self, mock_upy_retry, pymock, mocker: Mo...
    method test_read_file__error_chunk (line 276) | def test_read_file__error_chunk(self, mock_upy_retry, pymock, mocker: ...
    method test_pull_file (line 293) | def test_pull_file(self, pymock, tmp_path, mock_upy_retry):
    method test_iter_files (line 312) | def test_iter_files(self, pymock):
  class TestPyDevice (line 335) | class TestPyDevice:
    method mock_backend (line 337) | def mock_backend(self, mocker: MockFixture):
    method path_type (line 350) | def path_type(self, request: pytest.FixtureRequest):
    method test_init (line 361) | def test_init(self, mock_backend, pyd_kwargs):
    method test_connect (line 373) | def test_connect(self, mock_backend):
    method test_disconnect (line 378) | def test_disconnect(self, mock_backend):
    method test_copy_from (line 383) | def test_copy_from(self, mock_backend, path_type, mocker):
    method test_copy_from__integrity (line 403) | def test_copy_from__integrity(self, mock_backend, path_type, mocker):
    method test_copy_to (line 423) | def test_copy_to(self, mock_backend, path_type, mocker):
    method test_remove (line 435) | def test_remove(self, mock_backend):
  class TestConsumers (line 441) | class TestConsumers:
    method test_progress_consumer (line 459) | def test_progress_consumer(self, mocker: MockFixture, on_desc, expecte...
    method test_delegate (line 469) | def test_delegate(self):
    method test_rsh_consumer (line 478) | def test_rsh_consumer(self):

FILE: tests/test_stub_source.py
  function test_stub_info_spec_locator (line 6) | def test_stub_info_spec_locator(shared_datadir):
  function test_stub_info_spec_locator__returns_location_on_fail (line 11) | def test_stub_info_spec_locator__returns_location_on_fail(tmp_path):
  function test_source_ready (line 15) | def test_source_ready(shared_datadir, test_urls, tmp_path, mocker, test_...
  function test_stub_repo_locator (line 38) | def test_stub_repo_locator(stub_repo):  # noqa

FILE: tests/test_stubs.py
  function mock_fware (line 9) | def mock_fware(mocker, shared_datadir):
  function test_stub_validation (line 15) | def test_stub_validation(shared_datadir):
  function test_bad_stub_validation (line 24) | def test_bad_stub_validation(shared_datadir, mocker):
  function test_bad_stub (line 36) | def test_bad_stub(tmp_path):
  function test_valid_stub (line 42) | def test_valid_stub(shared_datadir):
  function test_valid_fware_stub (line 76) | def test_valid_fware_stub(shared_datadir):
  function test_resolve_stub (line 84) | def test_resolve_stub(shared_datadir):
  function test_resolve_firmware (line 100) | def test_resolve_firmware(tmp_path, shared_datadir):
  function test_add_single_stub (line 111) | def test_add_single_stub(shared_datadir, tmp_path):
  function test_add_stubs_from_dir (line 120) | def test_add_stubs_from_dir(datadir, tmp_path):
  function test_add_with_resource (line 133) | def test_add_with_resource(datadir, mock_fware, tmp_path, mocker):
  function test_add_no_resource_no_dest (line 151) | def test_add_no_resource_no_dest(datadir, mock_fware):
  function test_loads_from_resource (line 158) | def test_loads_from_resource(datadir, mock_fware):
  function test_name_property (line 164) | def test_name_property(shared_datadir):
  function test_stub_resolve_link (line 177) | def test_stub_resolve_link(mock_mp_stubs, tmp_path):
  function test_manager_resolve_subresource (line 188) | def test_manager_resolve_subresource(mock_mp_stubs, tmp_path):
  function test_load_firmware_first (line 199) | def test_load_firmware_first(mocker, tmp_path, shared_datadir):
  function test_iter_by_firm_stubs (line 220) | def test_iter_by_firm_stubs(mocker):

FILE: tests/test_stubs/esp32_test_stub/frozen/ntptime.py
  function time (line 16) | def time():
  function settime (line 31) | def settime():

FILE: tests/test_stubs/esp32_test_stub/frozen/ntptime.pyi
  function time (line 7) | def time() -> Any: ...
  function settime (line 11) | def settime() -> None: ...

FILE: tests/test_stubs/esp32_test_stub/stubs/machine.py
  class ADC (line 8) | class ADC:
    method read (line 11) | def read():
  class I2C (line 20) | class I2C:

FILE: tests/test_stubs/esp8266_test_stub/frozen/ntptime.py
  function time (line 16) | def time():
  function settime (line 31) | def settime():

FILE: tests/test_stubs/esp8266_test_stub/frozen/ntptime.pyi
  function time (line 7) | def time() -> Any: ...
  function settime (line 11) | def settime() -> None: ...

FILE: tests/test_stubs/esp8266_test_stub/stubs/machine.py
  class ADC (line 8) | class ADC:
    method read (line 11) | def read():
  class I2C (line 20) | class I2C:

FILE: tests/test_stubs_repo.py
  class ManifestStub (line 5) | class ManifestStub(StubsManifest[StubPackage]):
    method resolve_package_url (line 6) | def resolve_package_url(self, package: StubPackage) -> str:
  function stub_repo (line 39) | def stub_repo():
  function test_repo_inits (line 44) | def test_repo_inits(stub_repo):
  function test_repo_search (line 79) | def test_repo_search(stub_repo, query, expect_name, include_versions):

FILE: tests/test_template.py
  function stub_context (line 10) | def stub_context(mock_mp_stubs):
  function test_vscode_template (line 19) | def test_vscode_template(stub_context, shared_datadir, tmp_path, mock_ch...
  function test_pylint_template (line 71) | def test_pylint_template(stub_context, tmp_path):
  function test_generic_template (line 103) | def test_generic_template(mock_mp_stubs, tmp_path):
  function test_no_context (line 116) | def test_no_context():

FILE: tests/test_utils.py
  function schema (line 12) | def schema(datadir):
  function test_validate (line 19) | def test_validate(schema):
  function test_fail_validate (line 26) | def test_fail_validate(schema):
  function test_is_url (line 34) | def test_is_url(test_urls):
  function test_ensure_valid_url (line 43) | def test_ensure_valid_url(mocker, test_urls):
  function test_ensure_existing_dir (line 60) | def test_ensure_existing_dir(tmp_path):
  function test_is_downloadable (line 75) | def test_is_downloadable(mocker, test_urls):
  function test_get_url_filename (line 89) | def test_get_url_filename(test_urls):
  function test_is_existing_dir (line 96) | def test_is_existing_dir(tmp_path):
  function test_search_xml (line 105) | def test_search_xml(mocker, shared_datadir, test_urls):
  function test_generate_stub__py37 (line 118) | def test_generate_stub__py37(tmp_path):
  function test_prepare_create_stubs__py37 (line 126) | def test_prepare_create_stubs__py37():
  function test_prepare_create_stubs (line 132) | def test_prepare_create_stubs():
  function test_generate_stub (line 138) | def test_generate_stub(shared_datadir, tmp_path, mocker):
  function test_get_package_meta (line 150) | def test_get_package_meta(mocker, requests_mock):
  function test_extract_tarbytes (line 170) | def test_extract_tarbytes(mocker):
  function test_iter_requirements (line 182) | def test_iter_requirements(mocker, tmp_path):
  function test_create_dir_link (line 192) | def test_create_dir_link(mocker, tmp_path):
  function test_is_dir_link (line 222) | def test_is_dir_link(mocker, tmp_path):
  function test_is_update_available (line 255) | def test_is_update_available(mocker, requests_mock, versions, expect):
  function test_stream_download (line 264) | def test_stream_download(mocker):
Condensed preview — 165 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (461K chars).
[
  {
    "path": ".chglog/CHANGELOG.tpl.md",
    "chars": 1263,
    "preview": "{{ if .Versions -}}\n<a name=\"unreleased\"></a>\n## [Unreleased]\n\n{{ if .Unreleased.CommitGroups -}}\n{{ range .Unreleased.C"
  },
  {
    "path": ".chglog/config.yml",
    "chars": 816,
    "preview": "style: github\ntemplate: CHANGELOG.tpl.md\ninfo:\n  title: CHANGELOG\n  repository_url: https://github.com/BradenM/micropy-c"
  },
  {
    "path": ".editorconfig",
    "chars": 298,
    "preview": "# http://editorconfig.org\n\nroot = true\n\n[*]\nindent_style = space\nindent_size = 4\ntrim_trailing_whitespace = true\ninsert_"
  },
  {
    "path": ".git-blame-ignore-revs",
    "chars": 283,
    "preview": "# Format all files with pre-commit->black, isort, autoflake and other hooks\n80755a39f3fbd9983a07ff4571197b8f6dcae1cb\n# s"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 1508,
    "preview": "---\nname: Bug report\nabout: Create a report to help us improve Micropy Cli\ntitle: ''\nlabels: bug\nassignees: ''\n\n---\n\n<!-"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 207,
    "preview": "blank_issues_enabled: true\ncontact_links:\n    - name: Micropy Stubs\n      url: https://github.com/BradenM/micropy-stubs\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 708,
    "preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: enhancement\nassignees: ''\n\n---\n\n<!--"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/question.md",
    "chars": 96,
    "preview": "---\nname: Question\nabout: Got a question? Ask it!\ntitle: ''\nlabels: question\nassignees: ''\n\n---\n"
  },
  {
    "path": ".github/actions/setup-micropy/action.yml",
    "chars": 4141,
    "preview": "name: \"Setup Micropy\"\ndescription: \"Setup micropy CI env.\"\ninputs:\n  poetry-version:\n    description: Poetry version to "
  },
  {
    "path": ".github/codeql/codeql-config.yml",
    "chars": 155,
    "preview": "# do not scan entire .venv\n# security alters stemming from dependencies are handled elsewhere (dependabot alerts+github)"
  },
  {
    "path": ".github/renovate.json5",
    "chars": 807,
    "preview": "{\n    $schema: \"https://docs.renovatebot.com/renovate-schema.json\",\n    extends: [\n        \"config:base\",\n        \":reba"
  },
  {
    "path": ".github/workflows/changelog.yml",
    "chars": 1065,
    "preview": "name: Changelog\n\non:\n  push:\n    branches:\n      - master\n\njobs:\n  changelog:\n    name: Generate Changelog\n    runs-on: "
  },
  {
    "path": ".github/workflows/codeql-analysis.yml",
    "chars": 2290,
    "preview": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# Y"
  },
  {
    "path": ".github/workflows/main.yml",
    "chars": 1551,
    "preview": "name: Test MicropyCli\n\non:\n  pull_request: ~\n  push:\n    branches:\n      - master\n\nenv:\n  POETRY_VERSION: 1.8.3\n\nconcurr"
  },
  {
    "path": ".github/workflows/publish.yml",
    "chars": 644,
    "preview": "name: Publish Release\n\non:\n  release:\n    types:\n      - created\n\nenv:\n  POETRY_VERSION: 1.8.3\n  PYTHON_VERSION: 3.11\n\nj"
  },
  {
    "path": ".github/workflows/release.yml",
    "chars": 433,
    "preview": "name: Release\n\non:\n  workflow_dispatch:\n  push:\n    branches:\n      - master\n\njobs:\n  release-please:\n    name: Release "
  },
  {
    "path": ".gitignore",
    "chars": 4350,
    "preview": "\n# Created by https://www.gitignore.io/api/python\n# Edit at https://www.gitignore.io/?templates=python\n\n### Python ###\n#"
  },
  {
    "path": ".pre-commit-config.yaml",
    "chars": 1153,
    "preview": "# See https://pre-commit.com for more information\n# See https://pre-commit.com/hooks.html for more hooks\nexclude: |\n  (?"
  },
  {
    "path": ".prettierignore",
    "chars": 20,
    "preview": "micropy/template/**\n"
  },
  {
    "path": ".readthedocs.yml",
    "chars": 715,
    "preview": "# .readthedocs.yml\n# Read the Docs configuration file\n# See https://docs.readthedocs.io/en/stable/config-file/v2.html fo"
  },
  {
    "path": ".release-please-manifest.json",
    "chars": 21,
    "preview": "{\n    \".\": \"4.2.2\"\n}\n"
  },
  {
    "path": ".tool-versions",
    "chars": 53,
    "preview": "python     3.11.5\npoetry     1.8.3\ngit-chglog 0.15.2\n"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 48922,
    "preview": "<a name=\"v4.0.0\"></a>\n\n## [4.2.2](https://github.com/BradenM/micropy-cli/compare/v4.2.1...v4.2.2) (2023-06-14)\n\n\n### Bug"
  },
  {
    "path": "LICENSE",
    "chars": 1068,
    "preview": "MIT License\n\nCopyright (c) 2019 Braden Mars\n\nPermission is hereby granted, free of charge, to any person obtaining a cop"
  },
  {
    "path": "Makefile",
    "chars": 2173,
    "preview": ".PHONY: clean clean-test clean-pyc clean-build\nbold := $(shell tput bold)\nrsttxt := $(shell tput sgr0)\n\n\nclean: clean-bu"
  },
  {
    "path": "README.md",
    "chars": 9356,
    "preview": "# Micropy Cli [![PyPI][pypi-img]][pypi-url] [![PyPI - Python Version][pypiv-img]][pypi-url] [![Github - Test Micropy Cli"
  },
  {
    "path": "docs/Makefile",
    "chars": 634,
    "preview": "# Minimal makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line, and also\n# from the "
  },
  {
    "path": "docs/_autosummary/micropy.config.config_source.rst",
    "chars": 177,
    "preview": "micropy.config.config\\_source\n=============================\n\n.. automodule:: micropy.config.config_source\n\n\n\n\n\n\n\n   .. r"
  },
  {
    "path": "docs/_autosummary/micropy.config.rst",
    "chars": 173,
    "preview": "micropy.config\n==============\n\n.. automodule:: micropy.config\n\n\n\n\n\n\n\n   .. rubric:: Classes\n\n   .. autosummary::\n\n      "
  },
  {
    "path": "docs/_autosummary/micropy.exceptions.rst",
    "chars": 229,
    "preview": "micropy.exceptions\n==================\n\n.. automodule:: micropy.exceptions\n\n   \n   \n   \n\n   \n   \n   \n\n   \n   \n   .. rubri"
  },
  {
    "path": "docs/_autosummary/micropy.main.rst",
    "chars": 122,
    "preview": "micropy.main\n============\n\n.. automodule:: micropy.main\n\n\n\n\n\n\n\n   .. rubric:: Classes\n\n   .. autosummary::\n\n      MicroP"
  },
  {
    "path": "docs/_autosummary/micropy.packages.rst",
    "chars": 271,
    "preview": "micropy.packages\n================\n\n.. automodule:: micropy.packages\n\n\n\n   .. rubric:: Functions\n\n   .. autosummary::\n\n  "
  },
  {
    "path": "docs/_autosummary/micropy.project.modules.rst",
    "chars": 262,
    "preview": "micropy.project.modules\n=======================\n\n.. automodule:: micropy.project.modules\n\n\n\n\n\n\n\n   .. rubric:: Classes\n\n"
  },
  {
    "path": "docs/_autosummary/micropy.project.rst",
    "chars": 170,
    "preview": "micropy.project\n===============\n\n.. automodule:: micropy.project\n\n   \n   \n   \n\n   \n   \n   .. rubric:: Classes\n\n   .. aut"
  },
  {
    "path": "docs/_autosummary/micropy.rst",
    "chars": 80,
    "preview": "micropy\n=======\n\n.. automodule:: micropy\n\n   \n   \n   \n\n   \n   \n   \n\n   \n   \n   \n"
  },
  {
    "path": "docs/_autosummary/micropy.stubs.rst",
    "chars": 142,
    "preview": "micropy.stubs\n=============\n\n.. automodule:: micropy.stubs\n\n\n\n\n\n\n\n   .. rubric:: Classes\n\n   .. autosummary::\n\n      Stu"
  },
  {
    "path": "docs/_autosummary/micropy.stubs.source.rst",
    "chars": 274,
    "preview": "micropy.stubs.source\n====================\n\n.. automodule:: micropy.stubs.source\n\n\n\n   .. rubric:: Functions\n\n   .. autos"
  },
  {
    "path": "docs/_autosummary/micropy.utils.rst",
    "chars": 578,
    "preview": "micropy.utils\n=============\n\n.. automodule:: micropy.utils\n\n\n\n   .. rubric:: Functions\n\n   .. autosummary::\n\n      creat"
  },
  {
    "path": "docs/base.md",
    "chars": 7224,
    "preview": "## Installation\n\nYou can download and install the latest version of this software from the Python package index (PyPI) a"
  },
  {
    "path": "docs/cli.rst",
    "chars": 277,
    "preview": "CLI Usage\n=====\n\n.. click:: micropy.cli:cli\n   :prog: micropy\n\n.. click:: micropy.cli:init\n   :prog: micropy init\n   :sh"
  },
  {
    "path": "docs/conf.py",
    "chars": 2989,
    "preview": "# Configuration file for the Sphinx documentation builder.\n#\n# This file only contains a selection of the most common op"
  },
  {
    "path": "docs/header.rst",
    "chars": 1547,
    "preview": "Micropy Cli |PyPI| |PyPI - Python Version| |Github - Test Micropy Cli| |Coverage Status|\n==============================="
  },
  {
    "path": "docs/index.rst",
    "chars": 205,
    "preview": ".. include:: header.rst\n\n.. toctree::\n   :caption: Documentation\n   :maxdepth: 2\n\n   base.md\n   cli\n   modules\n\n\n\nIndice"
  },
  {
    "path": "docs/modules.rst",
    "chars": 296,
    "preview": "API Reference\n=============\n\n.. autosummary::\n   :toctree: _autosummary\n\n   micropy\n   micropy.main\n   micropy.exception"
  },
  {
    "path": "micropy/__init__.py",
    "chars": 503,
    "preview": "\"\"\"Micropy Cli.\n\nMicropy Cli is a project management/generation tool for writing Micropython\ncode in modern IDEs such as"
  },
  {
    "path": "micropy/__main__.py",
    "chars": 128,
    "preview": "from __future__ import annotations\n\nimport sys\n\nif __name__ == \"__main__\":\n    from micropy.cli import cli\n\n    sys.exit"
  },
  {
    "path": "micropy/app/__init__.py",
    "chars": 41,
    "preview": "from .main import app\n\n__all__ = [\"app\"]\n"
  },
  {
    "path": "micropy/app/main.py",
    "chars": 9765,
    "preview": "from __future__ import annotations\n\nfrom enum import Enum\nfrom pathlib import Path\nfrom typing import List, Optional, ca"
  },
  {
    "path": "micropy/app/stubs.py",
    "chars": 9695,
    "preview": "from __future__ import annotations\n\nimport sys\nimport tempfile\nfrom enum import Enum\nfrom pathlib import Path\nfrom typin"
  },
  {
    "path": "micropy/config/__init__.py",
    "chars": 225,
    "preview": "\"\"\"Configuration files and interfaces for them.\"\"\"\n\nfrom .config import Config\nfrom .config_dict import DictConfigSource"
  },
  {
    "path": "micropy/config/config.py",
    "chars": 5626,
    "preview": "from copy import deepcopy\nfrom typing import Any, List, Optional, Sequence, Tuple, Type, Union\n\nimport dpath\nfrom bolton"
  },
  {
    "path": "micropy/config/config_dict.py",
    "chars": 623,
    "preview": "from typing import Optional\n\nfrom .config_source import ConfigSource\n\n\nclass DictConfigSource(ConfigSource):\n    def __i"
  },
  {
    "path": "micropy/config/config_json.py",
    "chars": 1804,
    "preview": "import json\nfrom pathlib import Path\n\nfrom boltons.fileutils import AtomicSaver\n\nfrom .config_source import ConfigSource"
  },
  {
    "path": "micropy/config/config_source.py",
    "chars": 1595,
    "preview": "\"\"\"Config Abstract.\"\"\"\n\nimport abc\nimport contextlib\nfrom typing import Any, Optional\n\nfrom micropy.logger import Log, S"
  },
  {
    "path": "micropy/data/__init__.py",
    "chars": 622,
    "preview": "\"\"\"\nmicropy.data\n~~~~~~~~~~~~~~\n\nThis module is merely to provide an easy method of locating\ndata files used by MicropyC"
  },
  {
    "path": "micropy/data/schemas/firmware.json",
    "chars": 1849,
    "preview": "{\n    \"type\": \"object\",\n    \"required\": [\n        \"name\",\n        \"repo\",\n        \"firmware\",\n        \"modules\",\n       "
  },
  {
    "path": "micropy/data/schemas/stubs.json",
    "chars": 1812,
    "preview": "{\n    \"type\": \"object\",\n    \"required\": [\"firmware\", \"modules\"],\n    \"properties\": {\n        \"firmware\": {\n            \""
  },
  {
    "path": "micropy/data/sources.json",
    "chars": 409,
    "preview": "[\n    {\n        \"display_name\": \"BradenM/micropy-stubs\",\n        \"name\": \"micropy-stubs\",\n        \"source\": \"https://raw"
  },
  {
    "path": "micropy/exceptions.py",
    "chars": 2190,
    "preview": "\"\"\"Micropy Exceptions.\"\"\"\nfrom __future__ import annotations\n\n\nclass MicropyException(Exception):\n    \"\"\"Generic MicroPy"
  },
  {
    "path": "micropy/logger.py",
    "chars": 8894,
    "preview": "\"\"\"Logging functionality.\n\nTODO: Split logging from UI, refactor.\n\n\"\"\"\n\nimport logging\nimport re\nfrom contextlib import "
  },
  {
    "path": "micropy/main.py",
    "chars": 2902,
    "preview": "\"\"\"Main Module.\"\"\"\n\nfrom __future__ import annotations\n\nfrom pathlib import Path\nfrom typing import List, Optional\n\nimpo"
  },
  {
    "path": "micropy/packages/__init__.py",
    "chars": 1418,
    "preview": "\"\"\"Packages Module.\n\nAllows user to address different dependency types (package, module,\npath, pypi, etc.) through a sin"
  },
  {
    "path": "micropy/packages/package.py",
    "chars": 2987,
    "preview": "from pathlib import Path\nfrom typing import List, Optional, Tuple\n\nimport requirements\nfrom packaging.utils import canon"
  },
  {
    "path": "micropy/packages/source.py",
    "chars": 2045,
    "preview": "from contextlib import AbstractContextManager, ExitStack, contextmanager\nfrom pathlib import Path\nfrom typing import Lis"
  },
  {
    "path": "micropy/packages/source_package.py",
    "chars": 4752,
    "preview": "import shutil\nfrom pathlib import Path\nfrom tempfile import mkdtemp\nfrom typing import Any, Callable, List, Optional, Tu"
  },
  {
    "path": "micropy/packages/source_path.py",
    "chars": 789,
    "preview": "from pathlib import Path\nfrom typing import List, Optional, Tuple, Union\n\nfrom .package import Package\nfrom .source impo"
  },
  {
    "path": "micropy/project/__init__.py",
    "chars": 133,
    "preview": "\"\"\"Module for generating/managing projects.\"\"\"\n\nfrom . import modules\nfrom .project import Project\n\n__all__ = [\"Project\""
  },
  {
    "path": "micropy/project/checks.py",
    "chars": 2080,
    "preview": "\"\"\"Various requirement checks for templates.\"\"\"\n\nimport subprocess as subproc\nfrom functools import partial as _p\n\nfrom "
  },
  {
    "path": "micropy/project/modules/__init__.py",
    "chars": 339,
    "preview": "\"\"\"Project Modules.\"\"\"\n\n\nfrom .modules import HookProxy, ProjectModule\nfrom .packages import DevPackagesModule, Packages"
  },
  {
    "path": "micropy/project/modules/modules.py",
    "chars": 8354,
    "preview": "from __future__ import annotations\n\nimport abc\nimport inspect\nfrom copy import deepcopy\nfrom functools import wraps\nfrom"
  },
  {
    "path": "micropy/project/modules/packages.py",
    "chars": 7912,
    "preview": "\"\"\"Project Packages Module.\"\"\"\n\nimport shutil\nfrom pathlib import Path\nfrom typing import Any, Optional, Union\n\nfrom bol"
  },
  {
    "path": "micropy/project/modules/stubs.py",
    "chars": 4529,
    "preview": "\"\"\"Project Stubs Module.\"\"\"\n\nimport sys\nfrom pathlib import Path\nfrom typing import Any, List, Sequence, Union\n\nfrom bol"
  },
  {
    "path": "micropy/project/modules/templates.py",
    "chars": 2454,
    "preview": "\"\"\"Project Templates Module.\"\"\"\n\n\nfrom micropy.project.modules import ProjectModule\nfrom micropy.project.template import"
  },
  {
    "path": "micropy/project/project.py",
    "chars": 4659,
    "preview": "\"\"\"Hosts functionality relating to generation of user projects.\"\"\"\n\nfrom pathlib import Path\nfrom typing import Any, Ite"
  },
  {
    "path": "micropy/project/template/.gitignore",
    "chars": 2185,
    "preview": "\n# Created by https://www.gitignore.io/api/python,visualstudiocode\n# Edit at https://www.gitignore.io/?templates=python,"
  },
  {
    "path": "micropy/project/template/.pylintrc",
    "chars": 1330,
    "preview": "[MAIN]\n# Loaded Stubs: {% for stub in stubs %} {{ stub }} {% endfor %}\ninit-hook='import sys;sys.path[1:1]=[\"src/lib\",{%"
  },
  {
    "path": "micropy/project/template/.vscode/extensions.json",
    "chars": 488,
    "preview": "{\n    // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.\n    // Extension i"
  },
  {
    "path": "micropy/project/template/.vscode/settings.json",
    "chars": 803,
    "preview": "{\n    // Loaded Stubs: {% for stub in stubs %} {{ stub }} {% endfor %}\n    {% if language_server == 'pylance' %}\n    \"py"
  },
  {
    "path": "micropy/project/template/pymakr.conf",
    "chars": 431,
    "preview": "{\n    \"address\": \"192.168.4.1\",\n    \"username\": \"micro\",\n    \"password\": \"python\",\n    \"sync_folder\": \"src\",\n    \"open_o"
  },
  {
    "path": "micropy/project/template/src/boot.py",
    "chars": 30,
    "preview": "# boot.py - - runs on boot-up\n"
  },
  {
    "path": "micropy/project/template/src/main.py",
    "chars": 10,
    "preview": "# main.py\n"
  },
  {
    "path": "micropy/project/template.py",
    "chars": 10870,
    "preview": "\"\"\"Module for handling jinja2 and MicroPy Templates.\"\"\"\n\nimport json\nimport os\nfrom itertools import chain\nfrom pathlib "
  },
  {
    "path": "micropy/py.typed",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "micropy/pyd/__init__.py",
    "chars": 612,
    "preview": "\"\"\"Module for interfacing with py-devices.\"\"\"\n\nfrom .abc import (\n    DevicePath,\n    HostPath,\n    MessageConsumer,\n   "
  },
  {
    "path": "micropy/pyd/abc.py",
    "chars": 3771,
    "preview": "from __future__ import annotations\n\nimport abc\nfrom io import BytesIO, StringIO\nfrom pathlib import Path\nfrom typing imp"
  },
  {
    "path": "micropy/pyd/backend_rshell.py",
    "chars": 6795,
    "preview": "from __future__ import annotations\n\nfrom contextlib import contextmanager\nfrom pathlib import Path\nfrom typing import TY"
  },
  {
    "path": "micropy/pyd/backend_upydevice.py",
    "chars": 12055,
    "preview": "from __future__ import annotations\n\nimport binascii\nimport hashlib\nimport io\nimport random\nimport stat\nimport string\nimp"
  },
  {
    "path": "micropy/pyd/consumers.py",
    "chars": 2497,
    "preview": "from __future__ import annotations\n\nfrom functools import partialmethod\nfrom typing import Any, Callable, NamedTuple, ca"
  },
  {
    "path": "micropy/pyd/pydevice.py",
    "chars": 3096,
    "preview": "from __future__ import annotations\n\nfrom io import BytesIO, StringIO\nfrom pathlib import Path\nfrom typing import AnyStr,"
  },
  {
    "path": "micropy/stubs/__init__.py",
    "chars": 776,
    "preview": "\"\"\"\nmicropy.stubs\n~~~~~~~~~~~~~~\n\nThis module contains all functionality relating\nto stub files/frozen modules and their"
  },
  {
    "path": "micropy/stubs/manifest.py",
    "chars": 1227,
    "preview": "import abc\nfrom typing import FrozenSet, Generic\n\nfrom micropy.stubs.package import AnyStubPackage, StubPackage\nfrom mic"
  },
  {
    "path": "micropy/stubs/package.py",
    "chars": 384,
    "preview": "from __future__ import annotations\n\nfrom typing import TypeVar\n\nfrom pydantic import BaseModel\n\n\nclass StubPackage(BaseM"
  },
  {
    "path": "micropy/stubs/repo.py",
    "chars": 4916,
    "preview": "from __future__ import annotations\n\nimport collections\nimport inspect\nfrom typing import TYPE_CHECKING, ClassVar, Genera"
  },
  {
    "path": "micropy/stubs/repo_package.py",
    "chars": 1410,
    "preview": "from __future__ import annotations\n\nfrom typing import Iterator\n\nimport attrs\nfrom micropy.stubs import StubPackage, Stu"
  },
  {
    "path": "micropy/stubs/repositories/__init__.py",
    "chars": 269,
    "preview": "from .micropy import MicropyStubPackage, MicropyStubsManifest\nfrom .micropython import MicropythonStubsManifest, Micropy"
  },
  {
    "path": "micropy/stubs/repositories/micropy.py",
    "chars": 1049,
    "preview": "from __future__ import annotations\n\nfrom pathlib import PurePosixPath\nfrom urllib import parse\n\nfrom pydantic import Fie"
  },
  {
    "path": "micropy/stubs/repositories/micropython.py",
    "chars": 1354,
    "preview": "from __future__ import annotations\n\nimport functools\nfrom typing import TYPE_CHECKING\n\nfrom distlib.locators import loca"
  },
  {
    "path": "micropy/stubs/repository_info.py",
    "chars": 462,
    "preview": "from __future__ import annotations\n\nfrom datetime import timedelta\nfrom typing import Any\n\nimport requests\nfrom cachier "
  },
  {
    "path": "micropy/stubs/source.py",
    "chars": 4965,
    "preview": "\"\"\"\nmicropy.stubs.source\n~~~~~~~~~~~~~~\n\nThis module contains abstractions for handling stub sources\nand their location."
  },
  {
    "path": "micropy/stubs/stubs.py",
    "chars": 18672,
    "preview": "from __future__ import annotations\n\nimport json\nimport shutil\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\n"
  },
  {
    "path": "micropy/utils/__init__.py",
    "chars": 584,
    "preview": "\"\"\"\nmicropy.utils\n~~~~~~~~~~~~~~\n\nThis module provides utility functions that are used within\nMicropyCli.\n\"\"\"\n\nfrom .dec"
  },
  {
    "path": "micropy/utils/_compat.py",
    "chars": 302,
    "preview": "from __future__ import annotations\n\ntry:\n    from importlib import metadata as _metadata\nexcept ImportError:\n    # compa"
  },
  {
    "path": "micropy/utils/decorators.py",
    "chars": 382,
    "preview": "\"\"\"\nmicropy.utils.decorators\n~~~~~~~~~~~~~~\n\nThis module contains generic decorators\nused by MicropyCli\n\"\"\"\n\n__all__ = ["
  },
  {
    "path": "micropy/utils/helpers.py",
    "chars": 10918,
    "preview": "\"\"\"\nmicropy.utils.helpers\n~~~~~~~~~~~~~~\n\nThis module contains generic utility helpers\nused by MicropyCli\n\"\"\"\n\nfrom __fu"
  },
  {
    "path": "micropy/utils/stub.py",
    "chars": 3635,
    "preview": "\"\"\"Micropy stub utils.\"\"\"\nfrom __future__ import annotations\n\nimport importlib.util\nimport io\nimport sys\nfrom pathlib im"
  },
  {
    "path": "micropy/utils/types.py",
    "chars": 389,
    "preview": "\"\"\"Type utilities and variables.\"\"\"\n\nfrom __future__ import annotations\n\nfrom os import PathLike\nfrom typing import Any,"
  },
  {
    "path": "micropy/utils/validate.py",
    "chars": 1054,
    "preview": "\"\"\"\nmicropy.utils.validate\n~~~~~~~~~~~~~~\n\nThis module contains utility functions for MicropyCli\nthat center on data val"
  },
  {
    "path": "pyproject.toml",
    "chars": 5838,
    "preview": "[tool.poetry]\nname = \"micropy-cli\"\nversion = \"4.2.2\"\ndescription = \"Micropython Project Management Tool with VSCode supp"
  },
  {
    "path": "release-please-config.json",
    "chars": 1667,
    "preview": "{\n    \"$schema\": \"https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json\",\n    \"release-ty"
  },
  {
    "path": "scripts/export-docs-reqs.sh",
    "chars": 221,
    "preview": "#!/usr/bin/env bash\n\nROOT=\"$(git rev-parse --show-toplevel)\"\n\npoetry plugin add poetry-export-plugin>/dev/null\n\npoetry e"
  },
  {
    "path": "tests/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/app/conftest.py",
    "chars": 1840,
    "preview": "from typing import NamedTuple\nfrom unittest.mock import MagicMock\n\nimport pytest\nfrom micropy import MicroPy, logger\nfro"
  },
  {
    "path": "tests/app/test_main.py",
    "chars": 8601,
    "preview": "from pathlib import Path\nfrom typing import List, NamedTuple, Optional\n\nimport pytest\nimport typer\nfrom micropy import u"
  },
  {
    "path": "tests/app/test_stubs.py",
    "chars": 6161,
    "preview": "from pathlib import Path\n\nimport pytest\nfrom micropy.app import stubs as stubs_app\nfrom micropy.app.stubs import stubs_a"
  },
  {
    "path": "tests/conftest.py",
    "chars": 10049,
    "preview": "\"\"\" Common Pytest Fixtures\"\"\"\n\nimport importlib\nimport shutil\nfrom pathlib import Path\nfrom pprint import PrettyPrinter\n"
  },
  {
    "path": "tests/data/esp32_test_stub/frozen/ntptime.py",
    "chars": 882,
    "preview": "try:\n    import usocket as socket\nexcept:\n    import socket\ntry:\n    import ustruct as struct\nexcept:\n    import struct\n"
  },
  {
    "path": "tests/data/esp32_test_stub/frozen/ntptime.pyi",
    "chars": 231,
    "preview": "# make_stub_files: Fri 21 Jun 2019 at 00:44:00\n\nfrom typing import Any, Dict, Optional, Sequence, Tuple, Union\n\nNode = A"
  },
  {
    "path": "tests/data/esp32_test_stub/info.json",
    "chars": 553,
    "preview": "{\n    \"firmware\": {\n        \"machine\": \"ESP32 module with ESP32\",\n        \"firmware\": \"esp32 1.11.0\",\n        \"nodename\""
  },
  {
    "path": "tests/data/esp32_test_stub/stubs/machine.py",
    "chars": 347,
    "preview": "\"\"\"\nModule: 'machine' on esp8266 v1.9.4\n\"\"\"\n# MCU: (sysname='esp8266', nodename='esp8266', release='2.2.0-dev(9422289)',"
  },
  {
    "path": "tests/data/esp32_test_stub/stubs/modules.json",
    "chars": 3346,
    "preview": "[\n  {\n    \"nodename\": \"esp8266\",\n    \"release\": \"2.2.0-dev(9422289)\",\n    \"version\": \"v1.9.4-8-ga9a3caad0 on 2018-05-11\""
  },
  {
    "path": "tests/data/esp8266_invalid_stub/info.json",
    "chars": 157,
    "preview": "[\n    {\n        \"nodename\": \"esp32\",\n        \"release\": \"1.10.0\"\n    },\n    {\n        \"pathtofile\": \"/foobar/foo/bar.py\""
  },
  {
    "path": "tests/data/esp8266_test_stub/frozen/ntptime.py",
    "chars": 882,
    "preview": "try:\n    import usocket as socket\nexcept:\n    import socket\ntry:\n    import ustruct as struct\nexcept:\n    import struct\n"
  },
  {
    "path": "tests/data/esp8266_test_stub/frozen/ntptime.pyi",
    "chars": 231,
    "preview": "# make_stub_files: Fri 21 Jun 2019 at 00:44:00\n\nfrom typing import Any, Dict, Optional, Sequence, Tuple, Union\n\nNode = A"
  },
  {
    "path": "tests/data/esp8266_test_stub/info.json",
    "chars": 570,
    "preview": "{\n    \"firmware\": {\n        \"machine\": \"ESP module with ESP8266\",\n        \"firmware\": \"esp8266 v1.9.4\",\n        \"nodenam"
  },
  {
    "path": "tests/data/esp8266_test_stub/stubs/machine.py",
    "chars": 347,
    "preview": "\"\"\"\nModule: 'machine' on esp8266 v1.9.4\n\"\"\"\n# MCU: (sysname='esp8266', nodename='esp8266', release='2.2.0-dev(9422289)',"
  },
  {
    "path": "tests/data/esp8266_test_stub/stubs/modules.json",
    "chars": 3346,
    "preview": "[\n  {\n    \"nodename\": \"esp8266\",\n    \"release\": \"2.2.0-dev(9422289)\",\n    \"version\": \"v1.9.4-8-ga9a3caad0 on 2018-05-11\""
  },
  {
    "path": "tests/data/fware_test_stub/frozen/utarfile.py",
    "chars": 2219,
    "preview": "import uctypes\n\n# http://www.gnu.org/software/tar/manual/html_node/Standard.html\nTAR_HEADER = {\n    \"name\": (uctypes.ARR"
  },
  {
    "path": "tests/data/fware_test_stub/frozen/utarfile.pyi",
    "chars": 1276,
    "preview": "# make_stub_files: Thu 20 Jun 2019 at 23:08:04\n\nfrom typing import Any, Dict, Optional, Sequence, Tuple, Union\n\nNode = A"
  },
  {
    "path": "tests/data/fware_test_stub/frozen/utokenize.py",
    "chars": 1868,
    "preview": "# (c) 2019 Paul Sokolovsky, MIT license\nfrom token import *\n\nfrom ucollections import namedtuple\n\nNL = 55\nENCODING = 56\n"
  },
  {
    "path": "tests/data/fware_test_stub/frozen/utokenize.pyi",
    "chars": 313,
    "preview": "# make_stub_files: Thu 20 Jun 2019 at 23:08:04\n\nfrom typing import Any, Dict, Optional, Sequence, Tuple, Union\n\nNode = A"
  },
  {
    "path": "tests/data/fware_test_stub/info.json",
    "chars": 1331,
    "preview": "{\n    \"scope\": \"firmware\",\n    \"name\": \"MicroPython Official\",\n    \"repo\": \"micropython/micropython\",\n    \"module_path\":"
  },
  {
    "path": "tests/data/project_test/.pylintrc",
    "chars": 483,
    "preview": "[MASTER]\n# Loaded Stubs:  esp32-micropython-1.11.0  esp8266-micropython-1.11.0\ninit-hook='import sys;sys.path.insert(1, "
  },
  {
    "path": "tests/data/project_test/.vscode/settings.json",
    "chars": 1900,
    "preview": "{\n    \"python.linting.enabled\": true,\n\n    // Loaded Stubs:  esp32-micropython-1.11.0  esp8266-micropython-1.11.0\n    \"p"
  },
  {
    "path": "tests/data/project_test/micropy.json",
    "chars": 334,
    "preview": "{\n    \"name\": \"NewProject\",\n    \"stubs\": {\n        \"esp32-micropython-1.11.0\": \"1.2.0\",\n        \"esp8266-micropython-1.1"
  },
  {
    "path": "tests/data/stubber_test_stub/micropython.py",
    "chars": 500,
    "preview": "\"\"\"\nModule: 'micropython' on esp32 1.11.0\n\"\"\"\n\n\n# MCU: (sysname='esp32', nodename='esp32', release='1.11.0', version='v1"
  },
  {
    "path": "tests/data/stubber_test_stub/modules.json",
    "chars": 401,
    "preview": "{\n    \"firmware\": {\n        \"machine\": \"ESP32 module with ESP32\",\n        \"firmware\": \"esp32 1.11.0\",\n        \"nodename\""
  },
  {
    "path": "tests/data/test_repo.json",
    "chars": 560,
    "preview": "{\n    \"name\": \"Test Repo\",\n    \"location\": \"www.google.com\",\n    \"source\": \"www.google.com/file.json\",\n    \"path\": \"tarb"
  },
  {
    "path": "tests/data/test_source.xml",
    "chars": 846,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ListBucketResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">\n    <Name>test"
  },
  {
    "path": "tests/data/test_sources.json",
    "chars": 94,
    "preview": "[\n    {\n        \"name\": \"Test Repo\",\n        \"source\": \"https://google.com/repo.json\"\n    }\n]\n"
  },
  {
    "path": "tests/test_checks.py",
    "chars": 474,
    "preview": "import subprocess\n\nfrom micropy.project import checks\n\n\ndef test_vscode_ext_min_version(mock_checks, mocker):\n    \"\"\"Tes"
  },
  {
    "path": "tests/test_config.py",
    "chars": 3458,
    "preview": "import json\n\nimport pytest\nfrom micropy import config\n\n\nclass TestConfig:\n    default = {\"one\": 1, \"two\": 2, \"sub\": {\"it"
  },
  {
    "path": "tests/test_highlevel.py",
    "chars": 5119,
    "preview": "import json\nimport shutil\nfrom copy import deepcopy\nfrom pathlib import Path\n\nimport pytest\nfrom micropy import project\n"
  },
  {
    "path": "tests/test_main.py",
    "chars": 1400,
    "preview": "import micropy.exceptions as exc\nimport pytest\nfrom micropy import data, main\n\n\ndef test_setup(mock_micropy_path):\n    \""
  },
  {
    "path": "tests/test_packages.py",
    "chars": 4760,
    "preview": "from pathlib import Path\n\nimport pytest\nfrom boltons.namedutils import namedlist\nfrom micropy import packages\n\nEXPPKG = "
  },
  {
    "path": "tests/test_project.py",
    "chars": 10082,
    "preview": "import shutil\n\nimport pytest\nfrom boltons import setutils\nfrom micropy import config, packages, project\nfrom micropy.exc"
  },
  {
    "path": "tests/test_pyd.py",
    "chars": 17567,
    "preview": "from __future__ import annotations\n\nimport hashlib\nimport stat\nimport sys\nfrom pathlib import PurePosixPath\nfrom typing "
  },
  {
    "path": "tests/test_stub_source.py",
    "chars": 1574,
    "preview": "from micropy.stubs import source\n\nfrom tests.test_stubs_repo import stub_repo  # noqa\n\n\ndef test_stub_info_spec_locator("
  },
  {
    "path": "tests/test_stubs/bad_test_stub/modules.json",
    "chars": 157,
    "preview": "[\n    {\n        \"nodename\": \"esp32\",\n        \"release\": \"1.10.0\"\n    },\n    {\n        \"pathtofile\": \"/foobar/foo/bar.py\""
  },
  {
    "path": "tests/test_stubs/esp32_test_stub/frozen/ntptime.py",
    "chars": 882,
    "preview": "try:\n    import usocket as socket\nexcept:\n    import socket\ntry:\n    import ustruct as struct\nexcept:\n    import struct\n"
  },
  {
    "path": "tests/test_stubs/esp32_test_stub/frozen/ntptime.pyi",
    "chars": 231,
    "preview": "# make_stub_files: Fri 21 Jun 2019 at 00:44:00\n\nfrom typing import Any, Dict, Optional, Sequence, Tuple, Union\n\nNode = A"
  },
  {
    "path": "tests/test_stubs/esp32_test_stub/info.json",
    "chars": 553,
    "preview": "{\n    \"firmware\": {\n        \"machine\": \"ESP32 module with ESP32\",\n        \"firmware\": \"esp32 1.11.0\",\n        \"nodename\""
  },
  {
    "path": "tests/test_stubs/esp32_test_stub/stubs/machine.py",
    "chars": 347,
    "preview": "\"\"\"\nModule: 'machine' on esp8266 v1.9.4\n\"\"\"\n# MCU: (sysname='esp8266', nodename='esp8266', release='2.2.0-dev(9422289)',"
  },
  {
    "path": "tests/test_stubs/esp32_test_stub/stubs/modules.json",
    "chars": 3346,
    "preview": "[\n  {\n    \"nodename\": \"esp8266\",\n    \"release\": \"2.2.0-dev(9422289)\",\n    \"version\": \"v1.9.4-8-ga9a3caad0 on 2018-05-11\""
  },
  {
    "path": "tests/test_stubs/esp8266_test_stub/frozen/ntptime.py",
    "chars": 882,
    "preview": "try:\n    import usocket as socket\nexcept:\n    import socket\ntry:\n    import ustruct as struct\nexcept:\n    import struct\n"
  },
  {
    "path": "tests/test_stubs/esp8266_test_stub/frozen/ntptime.pyi",
    "chars": 231,
    "preview": "# make_stub_files: Fri 21 Jun 2019 at 00:44:00\n\nfrom typing import Any, Dict, Optional, Sequence, Tuple, Union\n\nNode = A"
  },
  {
    "path": "tests/test_stubs/esp8266_test_stub/info.json",
    "chars": 570,
    "preview": "{\n    \"firmware\": {\n        \"machine\": \"ESP module with ESP8266\",\n        \"firmware\": \"esp8266 v1.9.4\",\n        \"nodenam"
  },
  {
    "path": "tests/test_stubs/esp8266_test_stub/stubs/machine.py",
    "chars": 347,
    "preview": "\"\"\"\nModule: 'machine' on esp8266 v1.9.4\n\"\"\"\n# MCU: (sysname='esp8266', nodename='esp8266', release='2.2.0-dev(9422289)',"
  },
  {
    "path": "tests/test_stubs/esp8266_test_stub/stubs/modules.json",
    "chars": 3346,
    "preview": "[\n  {\n    \"nodename\": \"esp8266\",\n    \"release\": \"2.2.0-dev(9422289)\",\n    \"version\": \"v1.9.4-8-ga9a3caad0 on 2018-05-11\""
  },
  {
    "path": "tests/test_stubs.py",
    "chars": 8512,
    "preview": "import shutil\nfrom pathlib import Path\n\nimport pytest\nfrom micropy import exceptions, stubs\n\n\n@pytest.fixture\ndef mock_f"
  },
  {
    "path": "tests/test_stubs_repo.py",
    "chars": 2447,
    "preview": "import pytest\nfrom micropy.stubs import RepositoryInfo, StubPackage, StubRepository, StubsManifest\n\n\nclass ManifestStub("
  },
  {
    "path": "tests/test_template.py",
    "chars": 4534,
    "preview": "import json\nfrom pathlib import Path\n\nimport pylint.lint\nimport pytest\nfrom micropy.project.template import Template, Te"
  },
  {
    "path": "tests/test_utils/fail.json",
    "chars": 157,
    "preview": "[\n    {\n        \"nodename\": \"esp32\",\n        \"release\": \"1.10.0\"\n    },\n    {\n        \"pathtofile\": \"/foobar/foo/bar.py\""
  },
  {
    "path": "tests/test_utils/pass.json",
    "chars": 395,
    "preview": "[\n    {\n        \"nodename\": \"esp32\",\n        \"release\": \"1.10.0\",\n        \"version\": \"'v1.10-247-g0fb15fc3f on 2019-03-2"
  },
  {
    "path": "tests/test_utils/schema.json",
    "chars": 1088,
    "preview": "{\n    \"type\": \"array\",\n    \"items\": {\n        \"oneOf\": [\n            {\n                \"type\": \"object\",\n               "
  },
  {
    "path": "tests/test_utils.py",
    "chars": 10261,
    "preview": "import io\nimport sys\n\nimport pytest\nimport requests\nfrom jsonschema import ValidationError\nfrom micropy import utils\nfro"
  }
]

About this extraction

This page contains the full source code of the BradenM/micropy-cli GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 165 files (418.7 KB), approximately 116.9k tokens, and a symbol index with 766 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!