diff --git a/.github/workflows/deploy-website.yml b/.github/workflows/deploy-website.yml new file mode 100644 index 0000000000..8b7b0c7b7e --- /dev/null +++ b/.github/workflows/deploy-website.yml @@ -0,0 +1,80 @@ +name: Deploy Website + +on: + push: + branches: + - master + schedule: + - cron: "0 0 * * *" + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Install uv + uses: astral-sh/setup-uv@v7 + with: + enable-cache: true + + - name: Install dependencies + run: uv sync --group build + + - name: Restore star data cache + id: cache-stars + uses: actions/cache/restore@v4 + with: + path: website/data/github_stars.json + key: github-stars-${{ github.run_id }} + restore-keys: github-stars- + + - name: Fetch GitHub stars + id: fetch-stars + continue-on-error: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: make fetch_github_stars + + - name: Save star data cache + if: steps.fetch-stars.outcome == 'success' + uses: actions/cache/save@v4 + with: + path: website/data/github_stars.json + key: github-stars-${{ github.run_id }} + + - name: Verify star data exists + run: | + if [ ! -f website/data/github_stars.json ]; then + echo "::error::github_stars.json not found. No cache and fetch failed or was skipped." + exit 1 + fi + echo "Star data found: $(wc -l < website/data/github_stars.json) lines" + + - name: Build site + run: make build + + - name: Upload artifact + uses: actions/upload-pages-artifact@v4 + with: + path: website/output/ + + deploy: + needs: build + runs-on: ubuntu-latest + environment: + name: github-pages + url: https://awesome-python.com/ + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index 096c327d69..dd9781c3e1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,16 @@ +# macOS .DS_Store +# python +.venv/ *.py[co] -docs/index.md -site/ +# website +website/output/ +website/data/ -# PyCharm IDE -.idea +# claude code +.claude/skills/ +.superpowers/ +.gstack/ +skills-lock.json diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d5dd034b42..d15d811d8f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,18 +1,5 @@ # Contributing -## Automatic Rejection - -PRs will be **closed** if: - -- Adding multiple projects in one PR -- Duplicate of existing entry or recently-closed PR -- Empty or placeholder PR descriptions -- Placed under an inappropriate category -- Project is archived or abandoned (no commits in 12+ months) -- No documentation or unclear use case -- Less than 100 GitHub stars without Hidden Gem justification -- Repository less than 3 months old - ## Quality Requirements All submissions must satisfy **ALL** of these: @@ -31,7 +18,7 @@ Your submission must meet **ONE** of the following criteria: ### 1. Industry Standard - The go-to tool that almost everyone uses for a specific use case -- Examples: Requests, Flask, Pandas, NumPy +- Examples: requests, flask, pandas, numpy - Limit: 1-3 tools per category ### 2. Rising Star @@ -39,7 +26,7 @@ Your submission must meet **ONE** of the following criteria: - Rapid growth: 5,000+ GitHub stars in less than 2 years - Significant community buzz and adoption - Solving problems in new or better ways -- Examples: FastAPI, Ruff, uv +- Examples: fastapi, ruff, uv ### 3. Hidden Gem @@ -52,10 +39,16 @@ Your submission must meet **ONE** of the following criteria: ## Entry Format Reference +**Use GitHub repository URLs** whenever possible. Projects linked to a GitHub repo are ranked higher on [awesome-python.com](https://awesome-python.com/). + +### Naming Convention + +Use the **PyPI package name** as the display name so developers can copy it directly to `pip install`. Check the canonical name at `https://pypi.org/pypi/{package}/json`. If the project is not on PyPI, use the GitHub repository name instead. + ### Standard Entry ```markdown -- [project-name](https://github.com/owner/repo) - Description ending with period. +- [pypi-name](https://github.com/owner/repo) - Description ending with period. ``` ### Standard Library Module @@ -87,8 +80,9 @@ Your submission must meet **ONE** of the following criteria: ## Adding a New Section 1. Add section description in italics: `*Libraries for doing X.*` -2. Add the section title to the Table of Contents -3. Keep sections in alphabetical order +2. Add the section under the appropriate thematic group (e.g., **AI & ML**, **Web**, **Data & Science**) +3. Add the section title to the Table of Contents under its group +4. Keep entries in alphabetical order within each category ## Review Process @@ -101,3 +95,16 @@ PRs are reviewed by automated tools and maintainers: 5. **Quality Check**: Meets acceptance criteria Search previous Pull Requests and Issues before submitting, as yours may be a duplicate. + +## Automatic Rejection + +PRs will be **closed** if: + +- Adding multiple projects in one PR +- Duplicate of existing entry or recently-closed PR +- Empty or placeholder PR descriptions +- Placed under an inappropriate category +- Project is archived or abandoned (no commits in 12+ months) +- No documentation or unclear use case +- Less than 100 GitHub stars without Hidden Gem justification +- Repository less than 3 months old diff --git a/Makefile b/Makefile index eda7a8ff3a..21a4c5d28b 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,24 @@ -site_install: - pip install -r requirements.txt +-include .env +export -site_link: - ln -sf $(CURDIR)/README.md $(CURDIR)/docs/index.md +install: + uv sync -site_preview: site_link - mkdocs serve +fetch_github_stars: + uv run python website/fetch_github_stars.py -site_build: site_link - mkdocs build +test: + uv run pytest website/tests/ -v -site_deploy: site_link - mkdocs gh-deploy --clean +build: + uv run python website/build.py + +preview: build + @echo "Check the website on http://localhost:8000" + uv run watchmedo shell-command \ + --patterns='*.md;*.html;*.css;*.js;*.py' \ + --recursive \ + --wait --drop \ + --command='uv run python website/build.py' \ + README.md website/templates website/static website/data & \ + python -m http.server -b 127.0.0.1 -d website/output/ 8000 diff --git a/README.md b/README.md index 340e28e763..cfbd830586 100644 --- a/README.md +++ b/README.md @@ -1,292 +1,183 @@ # Awesome Python -An opinionated list of awesome Python frameworks, libraries, software and resources. +An opinionated list of awesome Python frameworks, libraries, tools, software and resources. > The **#10 most-starred repo on GitHub**. Put your product where Python developers discover tools. [Become a sponsor](SPONSORSHIP.md). # Categories -- [Admin Panels](#admin-panels) -- [Algorithms and Design Patterns](#algorithms-and-design-patterns) -- [ASGI Servers](#asgi-servers) -- [Asynchronous Programming](#asynchronous-programming) -- [Audio](#audio) -- [Authentication](#authentication) -- [Build Tools](#build-tools) -- [Built-in Classes Enhancement](#built-in-classes-enhancement) -- [Caching](#caching) -- [CMS](#cms) -- [Code Analysis](#code-analysis) -- [Command-line Interface Development](#command-line-interface-development) -- [Command-line Tools](#command-line-tools) -- [Computer Vision](#computer-vision) -- [Configuration Files](#configuration-files) -- [Cryptography](#cryptography) -- [Data Analysis](#data-analysis) -- [Data Validation](#data-validation) -- [Data Visualization](#data-visualization) -- [Database Drivers](#database-drivers) -- [Database](#database) -- [Date and Time](#date-and-time) -- [Debugging Tools](#debugging-tools) +**AI & ML** + +- [AI and Agents](#ai-and-agents) - [Deep Learning](#deep-learning) -- [DevOps Tools](#devops-tools) -- [Distributed Computing](#distributed-computing) -- [Distribution](#distribution) -- [Documentation](#documentation) -- [Downloader](#downloader) -- [Editor Plugins and IDEs](#editor-plugins-and-ides) -- [Email](#email) -- [Environment Management](#environment-management) -- [File Manipulation](#file-manipulation) -- [Functional Programming](#functional-programming) -- [Game Development](#game-development) -- [Geolocation](#geolocation) -- [GUI Development](#gui-development) -- [Hardware](#hardware) -- [HTML Manipulation](#html-manipulation) -- [HTTP Clients](#http-clients) -- [Image Processing](#image-processing) -- [Implementations](#implementations) -- [Interactive Interpreter](#interactive-interpreter) -- [Internationalization](#internationalization) -- [Job Scheduler](#job-scheduler) -- [Logging](#logging) - [Machine Learning](#machine-learning) -- [Miscellaneous](#miscellaneous) - [Natural Language Processing](#natural-language-processing) -- [Network Virtualization](#network-virtualization) -- [ORM](#orm) -- [Package Management](#package-management) -- [Package Repositories](#package-repositories) -- [Penetration testing](#penetration-testing) -- [Permissions](#permissions) -- [Processes](#processes) -- [Quantum Computing](#quantum-computing) +- [Computer Vision](#computer-vision) - [Recommender Systems](#recommender-systems) -- [Refactoring](#refactoring) -- [RESTful API](#restful-api) -- [Robotics](#robotics) -- [RPC Servers](#rpc-servers) -- [Science](#science) -- [Search](#search) -- [Serialization](#serialization) -- [Serverless Frameworks](#serverless-frameworks) -- [Shell](#shell) -- [Specific Formats Processing](#specific-formats-processing) -- [Static Site Generator](#static-site-generator) -- [Task Queues](#task-queues) -- [Template Engine](#template-engine) -- [Testing](#testing) -- [Text Processing](#text-processing) -- [URL Manipulation](#url-manipulation) -- [Video](#video) -- [Web Asset Management](#web-asset-management) -- [Web Content Extracting](#web-content-extracting) -- [Web Crawling](#web-crawling) -- [Web Frameworks](#web-frameworks) -- [WebSocket](#websocket) -- [WSGI Servers](#wsgi-servers) -- [Resources](#resources) - - [Newsletters](#newsletters) - - [Podcasts](#podcasts) ---- +**Web** -## Admin Panels +- [Web Frameworks](#web-frameworks) +- [Web APIs](#web-apis) +- [Web Servers](#web-servers) +- [WebSocket](#websocket) +- [Template Engines](#template-engines) +- [Web Asset Management](#web-asset-management) +- [Authentication](#authentication) +- [Admin Panels](#admin-panels) +- [CMS](#cms) +- [Static Site Generators](#static-site-generators) -_Libraries for administrative interfaces._ +**HTTP & Scraping** -- [ajenti](https://github.com/ajenti/ajenti) - The admin panel your servers deserve. -- [django-grappelli](https://github.com/sehmaschine/django-grappelli) - A jazzy skin for the Django Admin-Interface. -- [django-unfold](https://github.com/unfoldadmin/django-unfold) - Elevate your Django admin with a stunning modern interface, powerful features, and seamless user experience. -- [flask-admin](https://github.com/flask-admin/flask-admin) - Simple and extensible administrative interface framework for Flask. -- [flower](https://github.com/mher/flower) - Real-time monitor and web admin for Celery. -- [func-to-web](https://github.com/offerrall/FuncToWeb) - Instantly create web UIs from Python functions using type hints. Zero frontend code required. -- [jet-bridge](https://github.com/jet-admin/jet-bridge) - Admin panel framework for any application with nice UI (ex Jet Django). -- [streamlit](https://github.com/streamlit/streamlit) - A framework which lets you build dashboards, generate reports, or create chat apps in minutes. -- [wooey](https://github.com/wooey/wooey) - A Django app which creates automatic web UIs for Python scripts. +- [HTTP Clients](#http-clients) +- [Web Scraping](#web-scraping) +- [Email](#email) -## Algorithms and Design Patterns +**Database & Storage** -_Python implementation of data structures, algorithms and design patterns. Also see [awesome-algorithms](https://github.com/tayllan/awesome-algorithms)._ +- [ORM](#orm) +- [Database Drivers](#database-drivers) +- [Database](#database) +- [Caching](#caching) +- [Search](#search) +- [Serialization](#serialization) -- Algorithms - - [algorithms](https://github.com/keon/algorithms) - Minimal examples of data structures and algorithms. - - [python-ds](https://github.com/prabhupant/python-ds) - A collection of data structure and algorithms for coding interviews. - - [sortedcontainers](https://github.com/grantjenks/python-sortedcontainers) - Fast and pure-Python implementation of sorted collections. - - [thealgorithms](https://github.com/TheAlgorithms/Python) - All Algorithms implemented in Python. -- Design Patterns - - [python-cqrs](https://github.com/vadikko2/python-cqrs) - Event-Driven Architecture Framework with CQRS/CQS, Transaction Outbox, Saga orchestration. - - [python-patterns](https://github.com/faif/python-patterns) - A collection of design patterns in Python. - - [transitions](https://github.com/pytransitions/transitions) - A lightweight, object-oriented finite state machine implementation. +**Data & Science** -## ASGI Servers +- [Data Analysis](#data-analysis) +- [Data Validation](#data-validation) +- [Data Visualization](#data-visualization) +- [Geolocation](#geolocation) +- [Science](#science) +- [Quantum Computing](#quantum-computing) -_[ASGI](https://asgi.readthedocs.io/en/latest/)-compatible web servers._ +**Developer Tools** -- [daphne](https://github.com/django/daphne) - A HTTP, HTTP2 and WebSocket protocol server for ASGI and ASGI-HTTP. -- [granian](https://github.com/emmett-framework/granian) - A Rust HTTP server for Python applications built on top of Hyper and Tokio, supporting WSGI/ASGI/RSGI. -- [hypercorn](https://github.com/pgjones/hypercorn) - An ASGI and WSGI Server based on Hyper libraries and inspired by Gunicorn. -- [uvicorn](https://github.com/encode/uvicorn) - A lightning-fast ASGI server implementation, using uvloop and httptools. +- [Algorithms and Design Patterns](#algorithms-and-design-patterns) +- [Interactive Interpreter](#interactive-interpreter) +- [Code Analysis](#code-analysis) +- [Testing](#testing) +- [Debugging Tools](#debugging-tools) +- [Build Tools](#build-tools) +- [Documentation](#documentation) +- [Editor Plugins and IDEs](#editor-plugins-and-ides) -## Asynchronous Programming +**DevOps** -_Libraries for asynchronous, concurrent and parallel execution. Also see [awesome-asyncio](https://github.com/timofurrer/awesome-asyncio)._ +- [DevOps Tools](#devops-tools) +- [Distributed Computing](#distributed-computing) +- [Task Queues](#task-queues) +- [Job Schedulers](#job-schedulers) +- [Logging](#logging) +- [Network Virtualization](#network-virtualization) +- [RPC Servers](#rpc-servers) -- [asyncio](https://docs.python.org/3/library/asyncio.html) - (Python standard library) Asynchronous I/O, event loop, coroutines and tasks. - - [awesome-asyncio](https://github.com/timofurrer/awesome-asyncio) -- [concurrent.futures](https://docs.python.org/3/library/concurrent.futures.html) - (Python standard library) A high-level interface for asynchronously executing callables. -- [gevent](https://github.com/gevent/gevent) - A coroutine-based Python networking library that uses [greenlet](https://github.com/python-greenlet/greenlet). -- [multiprocessing](https://docs.python.org/3/library/multiprocessing.html) - (Python standard library) Process-based parallelism. -- [trio](https://github.com/python-trio/trio) - A friendly library for async concurrency and I/O. -- [twisted](https://github.com/twisted/twisted) - An event-driven networking engine. -- [uvloop](https://github.com/MagicStack/uvloop) - Ultra fast asyncio event loop. +**CLI & GUI** -## Audio +- [Command-line Interface Development](#command-line-interface-development) +- [Command-line Tools](#command-line-tools) +- [GUI Development](#gui-development) -_Libraries for manipulating audio and its metadata._ +**Text & Documents** -- Audio - - [audioFlux](https://github.com/libAudioFlux/audioFlux) - A library for audio and music analysis, feature extraction. - - [audioread](https://github.com/beetbox/audioread) - Cross-library (GStreamer + Core Audio + MAD + FFmpeg) audio decoding. - - [dejavu](https://github.com/worldveil/dejavu) - Audio fingerprinting and recognition. - - [gTTS](https://github.com/pndurette/gTTS) - Python library and CLI tool for converting text to speech using Google Translate TTS. - - [kapre](https://github.com/keunwoochoi/kapre) - Keras Audio Preprocessors. - - [librosa](https://github.com/librosa/librosa) - Python library for audio and music analysis. - - [matchering](https://github.com/sergree/matchering) - A library for automated reference audio mastering. - - [mingus](http://bspaans.github.io/python-mingus/) - An advanced music theory and notation package with MIDI file and playback support. - - [pyaudioanalysis](https://github.com/tyiannak/pyAudioAnalysis) - Audio feature extraction, classification, segmentation and applications. - - [pydub](https://github.com/jiaaro/pydub) - Manipulate audio with a simple and easy high level interface. - - [timeside](https://github.com/Parisson/TimeSide) - Open web audio processing framework. -- Metadata - - [beets](https://github.com/beetbox/beets) - A music library manager and [MusicBrainz](https://musicbrainz.org/) tagger. - - [eyed3](https://github.com/nicfit/eyeD3) - A tool for working with audio files, specifically MP3 files containing ID3 metadata. - - [mutagen](https://github.com/quodlibet/mutagen) - A Python module to handle audio metadata. - - [tinytag](https://github.com/devsnd/tinytag) - A library for reading music meta data of MP3, OGG, FLAC and Wave files. +- [Text Processing](#text-processing) +- [HTML Manipulation](#html-manipulation) +- [File Format Processing](#file-format-processing) +- [File Manipulation](#file-manipulation) -## Authentication +**Media** -_Libraries for implementing authentication schemes._ +- [Image Processing](#image-processing) +- [Audio & Video Processing](#audio--video-processing) +- [Game Development](#game-development) -- OAuth - - [authlib](https://github.com/lepture/authlib) - JavaScript Object Signing and Encryption draft implementation. - - [django-allauth](https://github.com/pennersr/django-allauth) - Authentication app for Django that "just works." - - [django-oauth-toolkit](https://github.com/jazzband/django-oauth-toolkit) - OAuth 2 goodies for Django. - - [oauthlib](https://github.com/oauthlib/oauthlib) - A generic and thorough implementation of the OAuth request-signing logic. -- JWT - - [pyjwt](https://github.com/jpadilla/pyjwt) - JSON Web Token implementation in Python. - - [python-jose](https://github.com/mpdavis/python-jose/) - A JOSE implementation in Python. +**Python Language** -## Build Tools +- [Implementations](#implementations) +- [Built-in Classes Enhancement](#built-in-classes-enhancement) +- [Functional Programming](#functional-programming) +- [Asynchronous Programming](#asynchronous-programming) +- [Date and Time](#date-and-time) -_Compile software from source code._ +**Python Toolchain** -- [bitbake](https://github.com/openembedded/bitbake) - A make-like build tool for embedded Linux. -- [buildout](https://github.com/buildout/buildout) - A build system for creating, assembling and deploying applications from multiple parts. -- [platformio](https://github.com/platformio/platformio-core) - A console tool to build code with different development platforms. -- [pybuilder](https://github.com/pybuilder/pybuilder) - A continuous build tool written in pure Python. -- [scons](https://github.com/SCons/scons) - A software construction tool. +- [Environment Management](#environment-management) +- [Package Management](#package-management) +- [Package Repositories](#package-repositories) +- [Distribution](#distribution) +- [Configuration Files](#configuration-files) -## Built-in Classes Enhancement +**Security** -_Libraries for enhancing Python built-in classes._ +- [Cryptography](#cryptography) +- [Penetration Testing](#penetration-testing) -- [attrs](https://github.com/python-attrs/attrs) - Replacement for `__init__`, `__eq__`, `__repr__`, etc. boilerplate in class definitions. -- [bidict](https://github.com/jab/bidict) - Efficient, Pythonic bidirectional map data structures and related functionality. -- [box](https://github.com/cdgriffith/Box) - Python dictionaries with advanced dot notation access. -- [dataclasses](https://docs.python.org/3/library/dataclasses.html) - (Python standard library) Data classes. -- [dotteddict](https://github.com/carlosescri/DottedDict) - A library that provides a method of accessing lists and dicts with a dotted path notation. +**Miscellaneous** -## CMS +- [Hardware](#hardware) +- [Microsoft Windows](#microsoft-windows) +- [Miscellaneous](#miscellaneous) -_Content Management Systems._ +--- -- [feincms](https://github.com/feincms/feincms) - One of the most advanced Content Management Systems built on Django. -- [indico](https://github.com/indico/indico) - A feature-rich event management system, made @ [CERN](https://en.wikipedia.org/wiki/CERN). -- [wagtail](https://github.com/wagtail/wagtail) - A Django content management system. +**AI & ML** -## Caching +## AI and Agents -_Libraries for caching data._ +_Libraries for building AI applications, LLM integrations, and autonomous agents._ -- [beaker](https://github.com/bbangert/beaker) - A WSGI middleware for sessions and caching. -- [django-cache-machine](https://github.com/django-cache-machine/django-cache-machine) - Automatic caching and invalidation for Django models. -- [django-cacheops](https://github.com/Suor/django-cacheops) - A slick ORM cache with automatic granular event-driven invalidation. -- [dogpile.cache](https://github.com/sqlalchemy/dogpile.cache) - dogpile.cache is a next generation replacement for Beaker made by the same authors. -- [hermescache](https://pypi.org/project/HermesCache/) - Python caching library with tag-based invalidation and dogpile effect prevention. -- [pylibmc](https://github.com/lericson/pylibmc) - A Python wrapper around the [libmemcached](https://libmemcached.org/libMemcached.html) interface. -- [python-diskcache](https://github.com/grantjenks/python-diskcache) - SQLite and file backed cache backend with faster lookups than memcached and redis. +- Frameworks + - [autogen](https://github.com/microsoft/autogen) - A programming framework for building agentic AI applications. + - [crewai](https://github.com/crewAIInc/crewAI) - A framework for orchestrating role-playing autonomous AI agents for collaborative task solving. + - [dspy](https://github.com/stanfordnlp/dspy) - A framework for programming, not prompting, language models. + - [instructor](https://github.com/567-labs/instructor) - A library for extracting structured data from LLMs, powered by Pydantic. + - [langchain](https://github.com/langchain-ai/langchain) - Building applications with LLMs through composability. + - [llama_index](https://github.com/run-llama/llama_index) - A data framework for your LLM application. + - [pydantic-ai](https://github.com/pydantic/pydantic-ai) - A Python agent framework for building generative AI applications with structured schemas. +- Pretrained Models and Inference + - [diffusers](https://github.com/huggingface/diffusers) - A library that provides pretrained diffusion models for generating and editing images, audio, and video. + - [transformers](https://github.com/huggingface/transformers) - A framework that lets you easily use pretrained transformer models for NLP, vision, and audio tasks. + - [vllm](https://github.com/vllm-project/vllm) - A high-throughput and memory-efficient inference and serving engine for LLMs. -## Code Analysis +## Deep Learning -_Tools of static analysis, linters and code quality checkers. Also see [awesome-static-analysis](https://github.com/mre/awesome-static-analysis)._ +_Frameworks for Neural Networks and Deep Learning. Also see [awesome-deep-learning](https://github.com/ChristosChristofidis/awesome-deep-learning)._ -- Code Analysis - - [code-graph-rag](https://github.com/vitali87/code-graph-rag) - Builds knowledge graphs from multi-language codebases using Tree-sitter and Memgraph, enabling natural language querying of code structure. - - [code2flow](https://github.com/scottrogowski/code2flow) - Turn your Python and JavaScript code into DOT flowcharts. - - [prospector](https://github.com/PyCQA/prospector) - A tool to analyze Python code. - - [vulture](https://github.com/jendrikseipp/vulture) - A tool for finding and analyzing dead Python code. -- Code Linters - - [flake8](https://github.com/PyCQA/flake8) - A wrapper around `pycodestyle`, `pyflakes` and McCabe. - - [awesome-flake8-extensions](https://github.com/DmytroLitvinov/awesome-flake8-extensions) - - [pylint](https://github.com/pylint-dev/pylint) - A fully customizable source code analyzer. - - [ruff](https://github.com/astral-sh/ruff) - An extremely fast Python linter and code formatter. -- Code Formatters - - [black](https://github.com/psf/black) - The uncompromising Python code formatter. - - [isort](https://github.com/timothycrosley/isort) - A Python utility / library to sort imports. - - [yapf](https://github.com/google/yapf) - Yet another Python code formatter from Google. -- Static Type Checkers, also see [awesome-python-typing](https://github.com/typeddjango/awesome-python-typing) - - [mypy](https://github.com/python/mypy) - Check variable types during compile time. - - [pyre-check](https://github.com/facebook/pyre-check) - Performant type checking. - - [ty](https://github.com/astral-sh/ty) - An extremely fast Python type checker and language server. - - [typeshed](https://github.com/python/typeshed) - Collection of library stubs for Python, with static types. -- Static Type Annotations Generators - - [monkeytype](https://github.com/Instagram/MonkeyType) - A system for Python that generates static type annotations by collecting runtime types. - - [pytype](https://github.com/google/pytype) - Pytype checks and infers types for Python code - without requiring type annotations. +- [jax](https://github.com/jax-ml/jax) - a library for high-performance numerical computing with automatic differentiation and JIT compilation. +- [keras](https://github.com/keras-team/keras) - A high-level deep learning library with support for JAX, TensorFlow, and PyTorch backends. +- [pytorch-lightning](https://github.com/Lightning-AI/pytorch-lightning) - Deep learning framework to train, deploy, and ship AI products Lightning fast. +- [pytorch](https://github.com/pytorch/pytorch) - Tensors and Dynamic neural networks in Python with strong GPU acceleration. +- [stable-baselines3](https://github.com/DLR-RM/stable-baselines3) - PyTorch implementations of Stable Baselines (deep) reinforcement learning algorithms. +- [tensorflow](https://github.com/tensorflow/tensorflow) - The most popular Deep Learning framework created by Google. -## Command-line Interface Development +## Machine Learning -_Libraries for building command-line applications._ +_Libraries for Machine Learning. Also see [awesome-machine-learning](https://github.com/josephmisiti/awesome-machine-learning#python)._ -- Command-line Application Development - - [argparse](https://docs.python.org/3/library/argparse.html) - (Python standard library) Command-line option and argument parsing. - - [cement](https://github.com/datafolklabs/cement) - CLI Application Framework for Python. - - [click](https://github.com/pallets/click/) - A package for creating beautiful command line interfaces in a composable way. - - [cliff](https://github.com/openstack/cliff) - A framework for creating command-line programs with multi-level commands. - - [python-fire](https://github.com/google/python-fire) - A library for creating command line interfaces from absolutely any Python object. - - [python-prompt-toolkit](https://github.com/prompt-toolkit/python-prompt-toolkit) - A library for building powerful interactive command lines. - - [Typer](https://github.com/tiangolo/typer) - Modern CLI framework that uses Python type hints. Built on Click and Pydantic. -- Terminal Rendering - - [alive-progress](https://github.com/rsalmei/alive-progress) - A new kind of Progress Bar, with real-time throughput, eta and very cool animations. - - [asciimatics](https://github.com/peterbrittain/asciimatics) - A package to create full-screen text UIs (from interactive forms to ASCII animations). - - [bashplotlib](https://github.com/glamp/bashplotlib) - Making basic plots in the terminal. - - [colorama](https://github.com/tartley/colorama) - Cross-platform colored terminal text. - - [rich](https://github.com/Textualize/rich) - Python library for rich text and beautiful formatting in the terminal. Also provides a great `RichHandler` log handler. - - [textual](https://github.com/Textualize/textual) - A framework for building interactive user interfaces that run in the terminal and the browser. - - [tqdm](https://github.com/tqdm/tqdm) - Fast, extensible progress bar for loops and CLI. +- [catboost](https://github.com/catboost/catboost) - A fast, scalable, high performance gradient boosting on decision trees library. +- [feature_engine](https://github.com/feature-engine/feature_engine) - sklearn compatible API with the widest toolset for feature engineering and selection. +- [karateclub](https://github.com/benedekrozemberczki/karateclub) - Unsupervised machine learning toolbox for graph structured data. +- [h2o](https://github.com/h2oai/h2o-3) - Open Source Fast Scalable Machine Learning Platform. +- [lightgbm](https://github.com/lightgbm-org/LightGBM) - A fast, distributed, high performance gradient boosting framework. +- [mindsdb](https://github.com/mindsdb/mindsdb) - MindsDB is an open source AI layer for existing databases that allows you to effortlessly develop, train and deploy state-of-the-art machine learning models using standard queries. +- [pgmpy](https://github.com/pgmpy/pgmpy) - A Python library for probabilistic graphical models and Bayesian networks. +- [scikit-learn](https://github.com/scikit-learn/scikit-learn) - The most popular Python library for Machine Learning with extensive documentation and community support. +- [spark.ml](http://spark.apache.org/docs/latest/ml-guide.html) - [Apache Spark](http://spark.apache.org/)'s scalable Machine Learning library for distributed computing. +- [xgboost](https://github.com/dmlc/xgboost) - A scalable, portable, and distributed gradient boosting library. -## Command-line Tools +## Natural Language Processing -_Useful CLI-based tools for productivity._ +_Libraries for working with human languages._ -- Productivity Tools - - [ccb](https://github.com/bfly123/claude_code_bridge) - A CLI tool to orchestrate multiple LLMs (Claude, Gemini, etc.) in Tmux panes with cross-agent interaction. - - [cookiecutter](https://github.com/cookiecutter/cookiecutter) - A command-line utility that creates projects from cookiecutters (project templates). - - [copier](https://github.com/copier-org/copier) - A library and command-line utility for rendering projects templates. - - [doitlive](https://github.com/sloria/doitlive) - A tool for live presentations in the terminal. - - [howdoi](https://github.com/gleitz/howdoi) - Instant coding answers via the command line. - - [invoke](https://github.com/pyinvoke/invoke) - A tool for managing shell-oriented subprocesses and organizing executable Python code into CLI-invokable tasks. - - [pathpicker](https://github.com/facebook/PathPicker) - Select files out of bash output. - - [thefuck](https://github.com/nvbn/thefuck) - Correcting your previous console command. - - [tmuxp](https://github.com/tmux-python/tmuxp) - A [tmux](https://github.com/tmux/tmux) session manager. - - [try](https://github.com/timofurrer/try) - A dead simple CLI to try out python packages - it's never been easier. -- CLI Enhancements - - [httpie](https://github.com/httpie/cli) - A command line HTTP client, a user-friendly cURL replacement. - - [iredis](https://github.com/laixintao/iredis) - Redis CLI with autocompletion and syntax highlighting. - - [litecli](https://github.com/dbcli/litecli) - SQLite CLI with autocompletion and syntax highlighting. - - [mycli](https://github.com/dbcli/mycli) - MySQL CLI with autocompletion and syntax highlighting. - - [pgcli](https://github.com/dbcli/pgcli) - PostgreSQL CLI with autocompletion and syntax highlighting. +- General + - [gensim](https://github.com/piskvorky/gensim) - Topic Modeling for Humans. + - [nltk](https://github.com/nltk/nltk) - A leading platform for building Python programs to work with human language data. + - [spacy](https://github.com/explosion/spaCy) - A library for industrial-strength natural language processing in Python and Cython. + - [stanza](https://github.com/stanfordnlp/stanza) - The Stanford NLP Group's official Python library, supporting 60+ languages. +- Chinese + - [funnlp](https://github.com/fighting41love/funNLP) - A collection of tools and datasets for Chinese NLP. + - [jieba](https://github.com/fxsjy/jieba) - The most popular Chinese text segmentation library. ## Computer Vision @@ -294,316 +185,305 @@ _Libraries for Computer Vision._ - [easyocr](https://github.com/JaidedAI/EasyOCR) - Ready-to-use OCR with 40+ languages supported. - [kornia](https://github.com/kornia/kornia/) - Open Source Differentiable Computer Vision Library for PyTorch. -- [opencv](https://opencv.org/) - Open Source Computer Vision Library. +- [opencv](https://github.com/opencv/opencv-python) - Open Source Computer Vision Library. - [pytesseract](https://github.com/madmaze/pytesseract) - A wrapper for [Google Tesseract OCR](https://github.com/tesseract-ocr). -- [tesserocr](https://github.com/sirfz/tesserocr) - Another simple, Pillow-friendly, wrapper around the `tesseract-ocr` API for OCR. -## Configuration Files +## Recommender Systems -_Libraries for storing and parsing configuration options._ +_Libraries for building recommender systems._ -- [configobj](https://github.com/DiffSK/configobj) - INI file parser with validation. -- [configparser](https://docs.python.org/3/library/configparser.html) - (Python standard library) INI file parser. -- [dynaconf](https://github.com/dynaconf/dynaconf) - Dynaconf is a configuration manager with plugins for Django, Flask and FastAPI. -- [hydra](https://github.com/facebookresearch/hydra) - Hydra is a framework for elegantly configuring complex applications. -- [python-decouple](https://github.com/HBNetwork/python-decouple) - Strict separation of settings from code. +- [annoy](https://github.com/spotify/annoy) - Approximate Nearest Neighbors in C++/Python optimized for memory usage. +- [implicit](https://github.com/benfred/implicit) - A fast Python implementation of collaborative filtering for implicit datasets. +- [scikit-surprise](https://github.com/NicolasHug/Surprise) - A scikit for building and analyzing recommender systems. -## Cryptography +**Web** -- [cryptography](https://github.com/pyca/cryptography) - A package designed to expose cryptographic primitives and recipes to Python developers. -- [paramiko](https://github.com/paramiko/paramiko) - The leading native Python SSHv2 protocol library. -- [pynacl](https://github.com/pyca/pynacl) - Python binding to the Networking and Cryptography (NaCl) library. +## Web Frameworks -## Data Analysis +_Traditional full stack web frameworks. Also see [Web APIs](#web-apis)._ -_Libraries for data analysis._ +- Synchronous + - [bottle](https://github.com/bottlepy/bottle) - A fast and simple micro-framework distributed as a single file with no dependencies. + - [django](https://github.com/django/django) - The most popular web framework in Python. + - [awesome-django](https://github.com/shahraizali/awesome-django) + - [flask](https://github.com/pallets/flask) - A microframework for Python. + - [awesome-flask](https://github.com/humiaozuzu/awesome-flask) + - [pyramid](https://github.com/Pylons/pyramid) - A small, fast, down-to-earth, open source Python web framework. + - [awesome-pyramid](https://github.com/uralbash/awesome-pyramid) + - [fasthtml](https://github.com/AnswerDotAI/fasthtml) - The fastest way to create an HTML app. + - [awesome-fasthtml](https://github.com/amosgyamfi/awesome-fasthtml) + - [masonite](https://github.com/MasoniteFramework/masonite) - The modern and developer centric Python web framework. +- Asynchronous + - [litestar](https://github.com/litestar-org/litestar) - Production-ready, capable and extensible ASGI Web framework. + - [microdot](https://github.com/miguelgrinberg/microdot) - The impossibly small web framework for Python and MicroPython. + - [reflex](https://github.com/reflex-dev/reflex) – A framework for building reactive, full-stack web applications entirely with python . + - [robyn](https://github.com/sparckles/Robyn) - A high-performance async Python web framework with a Rust runtime. + - [starlette](https://github.com/Kludex/starlette) - A lightweight ASGI framework and toolkit for building high-performance async services. + - [tornado](https://github.com/tornadoweb/tornado) - A web framework and asynchronous networking library. -- [aws-sdk-pandas](https://github.com/aws/aws-sdk-pandas) - Pandas on AWS. -- [datasette](https://github.com/simonw/datasette) - An open source multi-tool for exploring and publishing data. -- [desbordante](https://github.com/desbordante/desbordante-core/) - An open source data profiler for complex pattern discovery. -- [docling](https://github.com/docling-project/docling) - Library for converting documents into structured data. -- [optimus](https://github.com/hi-primus/optimus) - Agile Data Science Workflows made easy with PySpark. -- [pandas](https://pandas.pydata.org/) - A library providing high-performance, easy-to-use data structures and data analysis tools. -- [pathway](https://github.com/pathwaycom/pathway) - Real-time data processing framework for Python with reactive dataflows. -- [polars](https://github.com/pola-rs/polars) - A fast DataFrame library implemented in Rust with a Python API. +## Web APIs -## Data Validation +_Libraries for building RESTful and GraphQL APIs._ -_Libraries for validating data. Used for forms in many cases._ +- Django + - [django-ninja](https://github.com/vitalik/django-ninja) - Fast, Django REST framework based on type hints and Pydantic. + - [django-rest-framework](https://github.com/encode/django-rest-framework) - A powerful and flexible toolkit to build web APIs. + - [strawberry-django](https://github.com/strawberry-graphql/strawberry-django) - Strawberry GraphQL integration with Django. +- Flask + - [apiflask](https://github.com/apiflask/apiflask) - A lightweight Python web API framework based on Flask and Marshmallow. +- Framework Agnostic + - [connexion](https://github.com/spec-first/connexion) - A spec-first framework that automatically handles requests based on your OpenAPI specification. + - [falcon](https://github.com/falconry/falcon) - A high-performance framework for building cloud APIs and web app backends. + - [fastapi](https://github.com/fastapi/fastapi) - A modern, fast, web framework for building APIs with standard Python type hints. + - [sanic](https://github.com/sanic-org/sanic) - A Python 3.6+ web server and web framework that's written to go fast. + - [strawberry](https://github.com/strawberry-graphql/strawberry) - A GraphQL library that leverages Python type annotations for schema definition. + - [webargs](https://github.com/marshmallow-code/webargs) - A friendly library for parsing HTTP request arguments with built-in support for popular web frameworks. -- [cerberus](https://github.com/pyeve/cerberus) - A lightweight and extensible data validation library. -- [colander](https://github.com/Pylons/colander) - Validating and deserializing data obtained via XML, JSON, an HTML form post. -- [jsonschema](https://github.com/python-jsonschema/jsonschema) - An implementation of [JSON Schema](http://json-schema.org/) for Python. -- [pydantic](https://github.com/pydantic/pydantic) - Data validation using Python type hints. -- [schema](https://github.com/keleshev/schema) - A library for validating Python data structures. -- [schematics](https://github.com/schematics/schematics) - Data Structure Validation. -- [voluptuous](https://github.com/alecthomas/voluptuous) - A Python data validation library. +## Web Servers -## Data Visualization +_ASGI and WSGI compatible web servers._ -_Libraries for visualizing data. Also see [awesome-javascript](https://github.com/sorrycc/awesome-javascript#data-visualization)._ +- ASGI + - [daphne](https://github.com/django/daphne) - A HTTP, HTTP2 and WebSocket protocol server for ASGI and ASGI-HTTP. + - [granian](https://github.com/emmett-framework/granian) - A Rust HTTP server for Python applications built on top of Hyper and Tokio, supporting WSGI/ASGI/RSGI. + - [hypercorn](https://github.com/pgjones/hypercorn) - An ASGI and WSGI Server based on Hyper libraries and inspired by Gunicorn. + - [uvicorn](https://github.com/Kludex/uvicorn) - A lightning-fast ASGI server implementation, using uvloop and httptools. +- WSGI + - [gunicorn](https://github.com/benoitc/gunicorn) - Pre-forked, ported from Ruby's Unicorn project. + - [uwsgi](https://github.com/unbit/uwsgi) - A project aims at developing a full stack for building hosting services, written in C. + - [waitress](https://github.com/Pylons/waitress) - Multi-threaded, powers Pyramid. -- [altair](https://github.com/altair-viz/altair) - Declarative statistical visualization library for Python. -- [bokeh](https://github.com/bokeh/bokeh) - Interactive Web Plotting for Python. -- [bqplot](https://github.com/bloomberg/bqplot) - Interactive Plotting Library for the Jupyter Notebook. -- [cartopy](https://github.com/SciTools/cartopy) - A cartographic python library with matplotlib support. -- [diagrams](https://github.com/mingrammer/diagrams) - Diagram as Code. -- [matplotlib](https://github.com/matplotlib/matplotlib) - A Python 2D plotting library. -- [plotly](https://github.com/plotly/plotly.py) - Interactive graphing library for Python. -- [plotnine](https://github.com/has2k1/plotnine) - A grammar of graphics for Python based on ggplot2. -- [pygal](https://github.com/Kozea/pygal) - A Python SVG Charts Creator. -- [pygraphviz](https://github.com/pygraphviz/pygraphviz/) - Python interface to [Graphviz](http://www.graphviz.org/). -- [pyqtgraph](https://github.com/pyqtgraph/pyqtgraph) - Interactive and realtime 2D/3D/Image plotting and science/engineering widgets. -- [seaborn](https://github.com/mwaskom/seaborn) - Statistical data visualization using Matplotlib. -- [UltraPlot](https://github.com/ultraplot/UltraPlot) - Matplotlib wrapper for publication-ready scientific figures with minimal code. Includes advanced subplot management, panel layouts, and batteries-included geoscience plotting. -- [vispy](https://github.com/vispy/vispy) - High-performance scientific visualization based on OpenGL. +## WebSocket -## Database +_Libraries for working with WebSocket._ -_Databases implemented in Python._ +- [autobahn-python](https://github.com/crossbario/autobahn-python) - WebSocket & WAMP for Python on Twisted and [asyncio](https://docs.python.org/3/library/asyncio.html). +- [channels](https://github.com/django/channels) - Developer-friendly asynchrony for Django. +- [flask-socketio](https://github.com/miguelgrinberg/Flask-SocketIO) - Socket.IO integration for Flask applications. +- [websockets](https://github.com/python-websockets/websockets) - A library for building WebSocket servers and clients with a focus on correctness and simplicity. -- [DuckDB](https://duckdb.org/) - An in-process SQL OLAP database management system; optimized for analytics and fast queries, similar to SQLite but for analytical workloads. -- [pickleDB](https://github.com/patx/pickledb) - A simple and lightweight key-value store for Python. -- [tinydb](https://github.com/msiemens/tinydb) - A tiny, document-oriented database. -- [zodb](https://github.com/zopefoundation/ZODB) - A native object database for Python. A key-value and object graph database. +## Template Engines -## Database Drivers +_Libraries and tools for templating and lexing._ -_Libraries for connecting and operating databases._ +- [jinja](https://github.com/pallets/jinja) - A modern and designer friendly templating language. +- [mako](https://github.com/sqlalchemy/mako) - Hyperfast and lightweight templating for the Python platform. -- MySQL - [awesome-mysql](http://shlomi-noach.github.io/awesome-mysql/) - - [mysqlclient](https://github.com/PyMySQL/mysqlclient) - MySQL connector with Python 3 support ([mysql-python](https://sourceforge.net/projects/mysql-python/) fork). - - [pymysql](https://github.com/PyMySQL/PyMySQL) - A pure Python MySQL driver compatible to mysql-python. -- PostgreSQL - [awesome-postgres](https://github.com/dhamaniasad/awesome-postgres) - - [psycopg](https://github.com/psycopg/psycopg) - The most popular PostgreSQL adapter for Python. -- SQlite - [awesome-sqlite](https://github.com/planetopendata/awesome-sqlite) - - [sqlite-utils](https://github.com/simonw/sqlite-utils) - Python CLI utility and library for manipulating SQLite databases. - - [sqlite3](https://docs.python.org/3/library/sqlite3.html) - (Python standard library) SQlite interface compliant with DB-API 2.0. -- Other Relational Databases - - [clickhouse-driver](https://github.com/mymarilyn/clickhouse-driver) - Python driver with native interface for ClickHouse. - - [pymssql](https://github.com/pymssql/pymssql) - A simple database interface to Microsoft SQL Server. -- NoSQL Databases - - [cassandra-driver](https://github.com/datastax/python-driver) - The Python Driver for Apache Cassandra. - - [Django MongoDB Backend](https://github.com/mongodb/django-mongodb-backend) - Official MongoDB database backend for Django. - - [kafka-python](https://github.com/dpkp/kafka-python) - The Python client for Apache Kafka. - - [pymongo](https://github.com/mongodb/mongo-python-driver) - The official Python client for MongoDB. - - [redis-py](https://github.com/redis/redis-py) - The Python client for Redis. - - [Beanie](https://github.com/BeanieODM/beanie) - An asynchronous Python object-document mapper (ODM) for MongoDB. +## Web Asset Management -## Date and Time +_Tools for managing, compressing and minifying website assets._ -_Libraries for working with dates and times._ +- [django-compressor](https://github.com/django-compressor/django-compressor) - Compresses linked and inline JavaScript or CSS into a single cached file. +- [django-pipeline](https://github.com/jazzband/django-pipeline) - An asset packaging library for Django. +- [django-storages](https://github.com/jschneier/django-storages) - A collection of custom storage back ends for Django. -- [arrow](https://github.com/arrow-py/arrow) - A Python library that offers a sensible and human-friendly approach to creating, manipulating, formatting and converting dates, times and timestamps. -- [dateutil](https://github.com/dateutil/dateutil) - Extensions to the standard Python [datetime](https://docs.python.org/3/library/datetime.html) module. -- [pendulum](https://github.com/sdispater/pendulum) - Python datetimes made easy. -- [pytz](https://pypi.org/project/pytz/) - World timezone definitions, modern and historical. Brings the [tz database](https://en.wikipedia.org/wiki/Tz_database) into Python. +## Authentication -## Debugging Tools +_Libraries for implementing authentication schemes._ -_Libraries for debugging code._ +- OAuth + - [authlib](https://github.com/authlib/authlib) - JavaScript Object Signing and Encryption draft implementation. + - [django-allauth](https://github.com/pennersr/django-allauth) - Authentication app for Django that "just works." + - [django-oauth-toolkit](https://github.com/django-oauth/django-oauth-toolkit) - OAuth 2 goodies for Django. + - [oauthlib](https://github.com/oauthlib/oauthlib) - A generic and thorough implementation of the OAuth request-signing logic. +- JWT + - [pyjwt](https://github.com/jpadilla/pyjwt) - JSON Web Token implementation in Python. +- Permissions + - [django-guardian](https://github.com/django-guardian/django-guardian) - Implementation of per object permissions for Django 1.2+ + - [django-rules](https://github.com/dfunckt/django-rules) - A tiny but powerful app providing object-level permissions to Django, without requiring a database. -- pdb-like Debugger - - [ipdb](https://github.com/gotcha/ipdb) - IPython-enabled [pdb](https://docs.python.org/3/library/pdb.html). - - [pudb](https://github.com/inducer/pudb) - A full-screen, console-based Python debugger. -- Tracing - - [manhole](https://github.com/ionelmc/python-manhole) - Debugging UNIX socket connections and present the stacktraces for all threads and an interactive prompt. - - [python-hunter](https://github.com/ionelmc/python-hunter) - A flexible code tracing toolkit. -- Profiler - - [py-spy](https://github.com/benfred/py-spy) - A sampling profiler for Python programs. Written in Rust. - - [vprof](https://github.com/nvdv/vprof) - Visual Python profiler. -- Others - - [django-debug-toolbar](https://github.com/jazzband/django-debug-toolbar) - Display various debug information for Django. - - [flask-debugtoolbar](https://github.com/pallets-eco/flask-debugtoolbar) - A port of the django-debug-toolbar to flask. - - [icecream](https://github.com/gruns/icecream) - Inspect variables, expressions, and program execution with a single, simple function call. - - [memory-graph](https://github.com/bterwijn/memory_graph) - Visualize Python data at runtime to debug references, mutability, and aliasing. - - [pyelftools](https://github.com/eliben/pyelftools) - Parsing and analyzing ELF files and DWARF debugging information. +## Admin Panels -## Deep Learning +_Libraries for administrative interfaces._ -_Frameworks for Neural Networks and Deep Learning. Also see [awesome-deep-learning](https://github.com/ChristosChristofidis/awesome-deep-learning)._ +- [ajenti](https://github.com/ajenti/ajenti) - The admin panel your servers deserve. +- [django-grappelli](https://github.com/sehmaschine/django-grappelli) - A jazzy skin for the Django Admin-Interface. +- [django-unfold](https://github.com/unfoldadmin/django-unfold) - Elevate your Django admin with a stunning modern interface, powerful features, and seamless user experience. +- [flask-admin](https://github.com/pallets-eco/flask-admin) - Simple and extensible administrative interface framework for Flask. +- [func-to-web](https://github.com/offerrall/FuncToWeb) - Instantly create web UIs from Python functions using type hints. Zero frontend code required. +- [jet-bridge](https://github.com/jet-admin/jet-bridge) - Admin panel framework for any application with nice UI (ex Jet Django). -- [jax](https://github.com/google/jax) - a library for high-performance numerical computing with automatic differentiation and JIT compilation. -- [keras](https://github.com/keras-team/keras) - A high-level neural networks library and capable of running on top of either TensorFlow or Theano. -- [pytorch-lightning](https://github.com/Lightning-AI/pytorch-lightning) - Deep learning framework to train, deploy, and ship AI products Lightning fast. -- [pytorch](https://github.com/pytorch/pytorch) - Tensors and Dynamic neural networks in Python with strong GPU acceleration. -- [stable-baselines3](https://github.com/DLR-RM/stable-baselines3) - PyTorch implementations of Stable Baselines (deep) reinforcement learning algorithms. -- [tensorflow](https://github.com/tensorflow/tensorflow) - The most popular Deep Learning framework created by Google. -- [vllm](https://github.com/vllm-project/vllm) - A high-throughput and memory-efficient inference and serving engine for LLMs. +## CMS -## DevOps Tools +_Content Management Systems._ -_Software and libraries for DevOps._ +- [django-cms](https://github.com/django-cms/django-cms) - The easy-to-use and developer-friendly enterprise CMS powered by Django. +- [indico](https://github.com/indico/indico) - A feature-rich event management system, made @ [CERN](https://en.wikipedia.org/wiki/CERN). +- [wagtail](https://github.com/wagtail/wagtail) - A Django content management system. -- Cloud Providers - - [boto3](https://github.com/boto/boto3) - Python interface to Amazon Web Services. -- Configuration Management - - [ansible](https://github.com/ansible/ansible) - A radically simple IT automation platform. - - [cloudinit](https://github.com/canonical/cloud-init) - A multi-distribution package that handles early initialization of a cloud instance. - - [openstack](https://www.openstack.org/) - Open source software for building private and public clouds. - - [pyinfra](https://github.com/pyinfra-dev/pyinfra) - A versatile CLI tools and python libraries to automate infrastructure. - - [saltstack](https://github.com/saltstack/salt) - Infrastructure automation and management system. -- SSH-style Deployment - - [cuisine](https://github.com/sebastien/cuisine) - Chef-like functionality for Fabric. - - [fabric](https://github.com/fabric/fabric) - A simple, Pythonic tool for remote execution and deployment. -- Process Management - - [supervisor](https://github.com/Supervisor/supervisor) - Supervisor process control system for UNIX. -- Monitoring - - [psutil](https://github.com/giampaolo/psutil) - A cross-platform process and system utilities module. -- Backup - - [borg](https://github.com/borgbackup/borg) - A deduplicating archiver with compression and encryption. -- Chaos Engineering - - [chaostoolkit](https://github.com/chaostoolkit/chaostoolkit) - A Chaos Engineering toolkit & Orchestration for Developers. +## Static Site Generators -## Distributed Computing +_Static site generator is a software that takes some text + templates as input and produces HTML files on the output._ -_Frameworks and libraries for Distributed Computing._ +- [lektor](https://github.com/lektor/lektor) - An easy to use static CMS and blog engine. +- [nikola](https://github.com/getnikola/nikola) - A static website and blog generator. +- [pelican](https://github.com/getpelican/pelican) - Static site generator that supports Markdown and reST syntax. -- Batch Processing - - [dask](https://github.com/dask/dask) - A flexible parallel computing library for analytic computing. - - [luigi](https://github.com/spotify/luigi) - A module that helps you build complex pipelines of batch jobs. - - [mpi4py](https://github.com/mpi4py/mpi4py) - Python bindings for MPI. - - [PySpark](https://github.com/apache/spark) - [Apache Spark](https://spark.apache.org/) Python API. - - [Ray](https://github.com/ray-project/ray/) - A system for parallel and distributed Python that unifies the machine learning ecosystem. -- Stream Processing - - [streamparse](https://github.com/Parsely/streamparse) - Run Python code against real-time streams of data via [Apache Storm](http://storm.apache.org/). +**HTTP & Scraping** -## Distribution +## HTTP Clients -_Libraries to create packaged executables for release distribution._ +_Libraries for working with HTTP._ -- [cx_freeze](https://github.com/marcelotduarte/cx_Freeze) - It is a Python tool that converts Python scripts into standalone executables and installers for Windows, macOS, and Linux. -- [Nuitka](https://github.com/Nuitka/Nuitka) - Compiles Python programs into high-performance standalone executables (cross-platform, supports all Python versions). -- [py2app](https://github.com/ronaldoussoren/py2app) - Freezes Python scripts (Mac OS X). -- [py2exe](https://github.com/py2exe/py2exe) - Freezes Python scripts (Windows). -- [pyarmor](https://github.com/dashingsoft/pyarmor) - A tool used to obfuscate python scripts, bind obfuscated scripts to fixed machine or expire obfuscated scripts. -- [pyinstaller](https://github.com/pyinstaller/pyinstaller) - Converts Python programs into stand-alone executables (cross-platform). -- [shiv](https://github.com/linkedin/shiv) - A command line utility for building fully self-contained zipapps (PEP 441), but with all their dependencies included. +- [aiohttp](https://github.com/aio-libs/aiohttp) - Asynchronous HTTP client/server framework for asyncio and Python. +- [furl](https://github.com/gruns/furl) - A small Python library that makes parsing and manipulating URLs easy. +- [httpx](https://github.com/encode/httpx) - A next generation HTTP client for Python. +- [requests](https://github.com/psf/requests) - HTTP Requests for Humans. +- [urllib3](https://github.com/urllib3/urllib3) - A HTTP library with thread-safe connection pooling, file post support, sanity friendly. -## Documentation +## Web Scraping -_Libraries for generating project documentation._ +_Libraries to automate web scraping and extract web content._ -- [sphinx](https://github.com/sphinx-doc/sphinx/) - Python Documentation generator. - - [awesome-sphinxdoc](https://github.com/yoloseem/awesome-sphinxdoc) -- [pdoc](https://github.com/mitmproxy/pdoc) - Epydoc replacement to auto generate API documentation for Python libraries. +- Frameworks + - [browser-use](https://github.com/browser-use/browser-use) - Make websites accessible for AI agents with easy browser automation. + - [crawl4ai](https://github.com/unclecode/crawl4ai) - An open-source, LLM-friendly web crawler that provides lightning-fast, structured data extraction specifically designed for AI agents. + - [mechanicalsoup](https://github.com/MechanicalSoup/MechanicalSoup) - A Python library for automating interaction with websites. + - [scrapy](https://github.com/scrapy/scrapy) - A fast high-level screen scraping and web crawling framework. +- Content Extraction + - [feedparser](https://github.com/kurtmckee/feedparser) - Universal feed parser. + - [html2text](https://github.com/Alir3z4/html2text) - Convert HTML to Markdown-formatted text. + - [micawber](https://github.com/coleifer/micawber) - A small library for extracting rich content from URLs. + - [sumy](https://github.com/miso-belica/sumy) - A module for automatic summarization of text documents and HTML pages. + - [trafilatura](https://github.com/adbar/trafilatura) - A tool for gathering text and metadata from the web, with built-in content filtering. -## Downloader +## Email -_Libraries for downloading._ +_Libraries for sending and parsing email._ -- [akshare](https://github.com/jindaxiang/akshare) - A financial data interface library, built for human beings! -- [edgartools](https://github.com/dgunning/edgartools) - Library for downloading structured data from SEC EDGAR filings and XBRL financial statements. -- [OpenBB](https://github.com/OpenBB-finance/OpenBB) - A financial data platform for analysts, quants and AI agents. -- [s3cmd](https://github.com/s3tools/s3cmd) - A command line tool for managing Amazon S3 and CloudFront. -- [yfinance](https://github.com/ranaroussi/yfinance) - Easy Pythonic way to download market and financial data from Yahoo Finance. -- [youtube-dl](https://github.com/ytdl-org/youtube-dl/) - A command-line program to download videos from YouTube and other video sites. +- [modoboa](https://github.com/modoboa/modoboa) - A mail hosting and management platform including a modern Web UI. +- [yagmail](https://github.com/kootenpv/yagmail) - Yet another Gmail/SMTP client. -## Editor Plugins and IDEs +**Database & Storage** -- Emacs - - [elpy](https://github.com/jorgenschaefer/elpy) - Emacs Python Development Environment. -- Vim - - [jedi-vim](https://github.com/davidhalter/jedi-vim) - Vim bindings for the Jedi auto-completion library for Python. - - [python-mode](https://github.com/python-mode/python-mode) - An all in one plugin for turning Vim into a Python IDE. - - [YouCompleteMe](https://github.com/Valloric/YouCompleteMe) - Includes [Jedi](https://github.com/davidhalter/jedi)-based completion engine for Python. -- Visual Studio - - [PTVS](https://github.com/Microsoft/PTVS) - Python Tools for Visual Studio. -- Visual Studio Code - - [Python](https://marketplace.visualstudio.com/items?itemName=ms-python.python) - The official VSCode extension with rich support for Python. -- IDE - - [PyCharm](https://www.jetbrains.com/pycharm/) - Commercial Python IDE by JetBrains. Has free community edition available. - - [spyder](https://github.com/spyder-ide/spyder) - Open Source Python IDE. +## ORM -## Email +_Libraries that implement Object-Relational Mapping or data mapping techniques._ -_Libraries for sending and parsing email._ +- Relational Databases + - [django.db.models](https://docs.djangoproject.com/en/dev/topics/db/models/) - The Django ORM. + - [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) - The Python SQL Toolkit and Object Relational Mapper. + - [awesome-sqlalchemy](https://github.com/dahlia/awesome-sqlalchemy) + - [dataset](https://github.com/pudo/dataset) - Store Python dicts in a database - works with SQLite, MySQL, and PostgreSQL. + - [peewee](https://github.com/coleifer/peewee) - A small, expressive ORM. + - [pony](https://github.com/ponyorm/pony/) - ORM that provides a generator-oriented interface to SQL. + - [sqlmodel](https://github.com/fastapi/sqlmodel) - SQLModel is based on Python type annotations, and powered by Pydantic and SQLAlchemy. + - [tortoise-orm](https://github.com/tortoise/tortoise-orm) - An easy-to-use asyncio ORM inspired by Django, with relations support. +- NoSQL Databases + - [beanie](https://github.com/BeanieODM/beanie) - An asynchronous Python object-document mapper (ODM) for MongoDB. + - [mongoengine](https://github.com/MongoEngine/mongoengine) - A Python Object-Document-Mapper for working with MongoDB. + - [pynamodb](https://github.com/pynamodb/PynamoDB) - A Pythonic interface for [Amazon DynamoDB](https://aws.amazon.com/dynamodb/). -- Mail Servers - - [modoboa](https://github.com/modoboa/modoboa) - A mail hosting and management platform including a modern Web UI. -- Clients - - [imbox](https://github.com/martinrusev/imbox) - Python IMAP for Humans. - - [yagmail](https://github.com/kootenpv/yagmail) - Yet another Gmail/SMTP client. -- Others - - [flanker](https://github.com/mailgun/flanker) - An email address and Mime parsing library. - - [mailer](https://github.com/marrow/mailer) - High-performance extensible mail delivery framework. +## Database Drivers -## Environment Management +_Libraries for connecting and operating databases._ -_Libraries for Python version and virtual environment management._ +- MySQL - [awesome-mysql](https://github.com/shlomi-noach/awesome-mysql) + - [mysqlclient](https://github.com/PyMySQL/mysqlclient) - MySQL connector with Python 3 support ([mysql-python](https://sourceforge.net/projects/mysql-python/) fork). + - [pymysql](https://github.com/PyMySQL/PyMySQL) - A pure Python MySQL driver compatible to mysql-python. +- PostgreSQL - [awesome-postgres](https://github.com/dhamaniasad/awesome-postgres) + - [psycopg](https://github.com/psycopg/psycopg) - The most popular PostgreSQL adapter for Python. +- SQlite - [awesome-sqlite](https://github.com/planetopendata/awesome-sqlite) + - [sqlite-utils](https://github.com/simonw/sqlite-utils) - Python CLI utility and library for manipulating SQLite databases. + - [sqlite3](https://docs.python.org/3/library/sqlite3.html) - (Python standard library) SQlite interface compliant with DB-API 2.0. +- Other Relational Databases + - [clickhouse-driver](https://github.com/mymarilyn/clickhouse-driver) - Python driver with native interface for ClickHouse. + - [pymssql](https://github.com/pymssql/pymssql) - A simple database interface to Microsoft SQL Server. +- NoSQL Databases + - [cassandra-driver](https://github.com/apache/cassandra-python-driver) - The Python Driver for Apache Cassandra. + - [django-mongodb-backend](https://github.com/mongodb/django-mongodb-backend) - Official MongoDB database backend for Django. + - [pymongo](https://github.com/mongodb/mongo-python-driver) - The official Python client for MongoDB. + - [redis-py](https://github.com/redis/redis-py) - The Python client for Redis. -- [pyenv](https://github.com/pyenv/pyenv) - Simple Python version management. -- [pyenv-win](https://github.com/pyenv-win/pyenv-win) - Pyenv for Windows, Simple Python version management. -- [uv](https://github.com/astral-sh/uv) - An extremely fast Python package and project manager, written in Rust. -- [virtualenv](https://github.com/pypa/virtualenv) - A tool to create isolated Python environments. +## Database -## File Manipulation +_Databases implemented in Python._ -_Libraries for file manipulation._ +- [chromadb](https://github.com/chroma-core/chroma) - An open-source embedding database for building AI applications with embeddings and semantic search. +- [duckdb](https://github.com/duckdb/duckdb) - An in-process SQL OLAP database management system; optimized for analytics and fast queries, similar to SQLite but for analytical workloads. +- [pickledb](https://github.com/patx/pickledb) - A simple and lightweight key-value store for Python. +- [tinydb](https://github.com/msiemens/tinydb) - A tiny, document-oriented database. +- [ZODB](https://github.com/zopefoundation/ZODB) - A native object database for Python. A key-value and object graph database. -- [markitdown](https://github.com/microsoft/markitdown) - Python tool for converting files and office documents to Markdown. -- [mimetypes](https://docs.python.org/3/library/mimetypes.html) - (Python standard library) Map filenames to MIME types. -- [path.py](https://github.com/jaraco/path.py) - A module wrapper for [os.path](https://docs.python.org/3/library/os.path.html). -- [pathlib](https://docs.python.org/3/library/pathlib.html) - (Python standard library) A cross-platform, object-oriented path library. -- [python-magic](https://github.com/ahupp/python-magic) - A Python interface to the libmagic file type identification library. -- [watchdog](https://github.com/gorakhargosh/watchdog) - API and shell utilities to monitor file system events. -- [watchfiles](https://github.com/samuelcolvin/watchfiles) - Simple, modern and fast file watching and code reload in python. +## Caching -## Functional Programming +_Libraries for caching data._ + +- [cachetools](https://github.com/tkem/cachetools) - Extensible memoizing collections and decorators. +- [django-cacheops](https://github.com/Suor/django-cacheops) - A slick ORM cache with automatic granular event-driven invalidation. +- [dogpile.cache](https://github.com/sqlalchemy/dogpile.cache) - dogpile.cache is a next generation replacement for Beaker made by the same authors. +- [hermescache](https://pypi.org/project/HermesCache/) - Python caching library with tag-based invalidation and dogpile effect prevention. +- [pylibmc](https://github.com/lericson/pylibmc) - A Python wrapper around the [libmemcached](https://libmemcached.org/libMemcached.html) interface. +- [python-diskcache](https://github.com/grantjenks/python-diskcache) - SQLite and file backed cache backend with faster lookups than memcached and redis. + +## Search + +_Libraries and software for indexing and performing search queries on data._ + +- [django-haystack](https://github.com/django-haystack/django-haystack) - Modular search for Django. +- [elasticsearch-py](https://github.com/elastic/elasticsearch-py) - The official low-level Python client for [Elasticsearch](https://www.elastic.co/products/elasticsearch). +- [pysolr](https://github.com/django-haystack/pysolr) - A lightweight Python wrapper for [Apache Solr](https://lucene.apache.org/solr/). + +## Serialization + +_Libraries for serializing complex data types._ + +- [marshmallow](https://github.com/marshmallow-code/marshmallow) - A lightweight library for converting complex objects to and from simple Python datatypes. +- [msgpack](https://github.com/msgpack/msgpack-python) - MessagePack serializer implementation for Python. +- [orjson](https://github.com/ijl/orjson) - Fast, correct JSON library. + +**Data & Science** + +## Data Analysis -_Functional Programming with Python._ +_Libraries for data analysis._ -- [coconut](https://github.com/evhub/coconut) - A variant of Python built for simple, elegant, Pythonic functional programming. -- [cytoolz](https://github.com/pytoolz/cytoolz/) - Cython implementation of `Toolz`: High performance functional utilities. -- [functools](https://docs.python.org/3/library/functools.html) - (Python standard library) Higher-order functions and operations on callable objects. -- [funcy](https://github.com/Suor/funcy) - A fancy and practical functional tools. -- [more-itertools](https://github.com/erikrose/more-itertools) - More routines for operating on iterables, beyond `itertools`. -- [returns](https://github.com/dry-python/returns) - A set of type-safe monads, transformers, and composition utilities. -- [toolz](https://github.com/pytoolz/toolz) - A collection of functional utilities for iterators, functions, and dictionaries. +- General + - [aws-sdk-pandas](https://github.com/aws/aws-sdk-pandas) - Pandas on AWS. + - [datasette](https://github.com/simonw/datasette) - An open source multi-tool for exploring and publishing data. + - [desbordante](https://github.com/desbordante/desbordante-core/) - An open source data profiler for complex pattern discovery. + - [ibis](https://github.com/ibis-project/ibis) - A portable Python dataframe library with a single API for 20+ backends. + - [modin](https://github.com/modin-project/modin) - A drop-in pandas replacement that scales workflows by changing a single line of code. + - [pandas](https://github.com/pandas-dev/pandas) - A library providing high-performance, easy-to-use data structures and data analysis tools. + - [pathway](https://github.com/pathwaycom/pathway) - Real-time data processing framework for Python with reactive dataflows. + - [polars](https://github.com/pola-rs/polars) - A fast DataFrame library implemented in Rust with a Python API. +- Financial Data + - [akshare](https://github.com/akfamily/akshare) - A financial data interface library, built for human beings! + - [edgartools](https://github.com/dgunning/edgartools) - Library for downloading structured data from SEC EDGAR filings and XBRL financial statements. + - [openbb](https://github.com/OpenBB-finance/OpenBB) - A financial data platform for analysts, quants and AI agents. + - [yfinance](https://github.com/ranaroussi/yfinance) - Easy Pythonic way to download market and financial data from Yahoo Finance. -## GUI Development +## Data Validation -_Libraries for working with graphical user interface applications._ +_Libraries for validating data. Used for forms in many cases._ -- [curses](https://docs.python.org/3/library/curses.html) - Built-in wrapper for [ncurses](http://www.gnu.org/software/ncurses/) used to create terminal GUI applications. -- [CustomTkinter](https://github.com/tomschimansky/customtkinter) - A modern and customizable python UI-library based on Tkinter. -- [DearPyGui](https://github.com/RaylockLLC/DearPyGui/) - A Simple GPU accelerated Python GUI framework -- [enaml](https://github.com/nucleic/enaml) - Creating beautiful user-interfaces with Declarative Syntax like QML. -- [Flet](https://flet.dev) - Cross-platform GUI framework for building modern apps in pure Python. -- [Flexx](https://github.com/zoofIO/flexx) - Flexx is a pure Python toolkit for creating GUI's, that uses web technology for its rendering. -- [Gooey](https://github.com/chriskiehl/Gooey) - Turn command line programs into a full GUI application with one line. -- [kivy](https://kivy.org/) - A library for creating NUI applications, running on Windows, Linux, Mac OS X, Android and iOS. -- [NiceGUI](https://github.com/zauberzeug/nicegui) - An easy-to-use, Python-based UI framework, which shows up in your web browser. -- [pyglet](https://github.com/pyglet/pyglet) - A cross-platform windowing and multimedia library for Python. -- [PyGObject](https://pygobject.readthedocs.io/) - Python Bindings for GLib/GObject/GIO/GTK+ (GTK+3). -- [PySide](https://doc.qt.io/qtforpython/) - Qt for Python offers the official Python bindings for [Qt](https://www.qt.io/), this is same as PyQt but it's the official binding with different licensing. -- [PyQt](https://www.riverbankcomputing.com/static/Docs/PyQt6/) - Python bindings for the [Qt](https://www.qt.io/) cross-platform application and UI framework. -- [pywebview](https://github.com/r0x0r/pywebview/) - A lightweight cross-platform native wrapper around a webview component. -- [Tkinter](https://wiki.python.org/moin/TkInter) - Tkinter is Python's de-facto standard GUI package. -- [Toga](https://github.com/pybee/toga) - A Python native, OS native GUI toolkit. -- [urwid](https://urwid.org/) - A library for creating terminal GUI applications with strong support for widgets, events, rich colors, etc. -- [wxPython](https://wxpython.org/) - A blending of the wxWidgets C++ class library with the Python. - -## GraphQL - -_Libraries for working with GraphQL._ - -- [graphene](https://github.com/graphql-python/graphene/) - GraphQL framework for Python. -- [strawberry-django](https://github.com/strawberry-graphql/strawberry-django) - Strawberry GraphQL integration with Django. +- [cerberus](https://github.com/pyeve/cerberus) - A lightweight and extensible data validation library. +- [jsonschema](https://github.com/python-jsonschema/jsonschema) - An implementation of [JSON Schema](http://json-schema.org/) for Python. +- [pandera](https://github.com/unionai-oss/pandera) - A data validation library for dataframes, with support for pandas, polars, and Spark. +- [pydantic](https://github.com/pydantic/pydantic) - Data validation using Python type hints. -## Game Development +## Data Visualization -_Awesome game development libraries._ +_Libraries for visualizing data. Also see [awesome-javascript](https://github.com/sorrycc/awesome-javascript#data-visualization)._ -- [Arcade](https://api.arcade.academy/en/latest/) - Arcade is a modern Python framework for crafting games with compelling graphics and sound. -- [Cocos2d](https://www.cocos.com/en/cocos2d-x) - A framework for building 2D games, demos, and other graphical/interactive applications. -- [Harfang3D](http://www.harfang3d.com) - Python framework for 3D, VR and game development. -- [Panda3D](https://www.panda3d.org/) - 3D game engine developed by Disney. -- [Pygame](https://www.pygame.org/news.html) - Pygame is a set of Python modules designed for writing games. -- [PyOgre](http://www.ogre3d.org/tikiwiki/PyOgre) - Python bindings for the Ogre 3D render engine, can be used for games, simulations, anything 3D. -- [PyOpenGL](http://pyopengl.sourceforge.net/) - Python ctypes bindings for OpenGL and it's related APIs. -- [PySDL2](https://pysdl2.readthedocs.io) - A ctypes based wrapper for the SDL2 library. -- [RenPy](https://www.renpy.org/) - A Visual Novel engine. +- Plotting + - [altair](https://github.com/vega/altair) - Declarative statistical visualization library for Python. + - [bokeh](https://github.com/bokeh/bokeh) - Interactive Web Plotting for Python. + - [bqplot](https://github.com/bqplot/bqplot) - Interactive Plotting Library for the Jupyter Notebook. + - [matplotlib](https://github.com/matplotlib/matplotlib) - A Python 2D plotting library. + - [plotly](https://github.com/plotly/plotly.py) - Interactive graphing library for Python. + - [plotnine](https://github.com/has2k1/plotnine) - A grammar of graphics for Python based on ggplot2. + - [pygal](https://github.com/Kozea/pygal) - A Python SVG Charts Creator. + - [pyqtgraph](https://github.com/pyqtgraph/pyqtgraph) - Interactive and realtime 2D/3D/Image plotting and science/engineering widgets. + - [seaborn](https://github.com/mwaskom/seaborn) - Statistical data visualization using Matplotlib. + - [ultraplot](https://github.com/ultraplot/UltraPlot) - Matplotlib wrapper for publication-ready scientific figures with minimal code. Includes advanced subplot management, panel layouts, and batteries-included geoscience plotting. + - [vispy](https://github.com/vispy/vispy) - High-performance scientific visualization based on OpenGL. +- Specialized + - [cartopy](https://github.com/SciTools/cartopy) - A cartographic python library with matplotlib support. + - [pygraphviz](https://github.com/pygraphviz/pygraphviz/) - Python interface to [Graphviz](http://www.graphviz.org/). +- Dashboards and Apps + - [gradio](https://github.com/gradio-app/gradio) - Build and share machine learning apps, all in Python. + - [streamlit](https://github.com/streamlit/streamlit) - A framework which lets you build dashboards, generate reports, or create chat apps in minutes. ## Geolocation @@ -615,578 +495,606 @@ _Libraries for geocoding addresses and working with latitudes and longitudes._ - [geopandas](https://github.com/geopandas/geopandas) - Python tools for geographic data (GeoSeries/GeoDataFrame) built on pandas. - [geopy](https://github.com/geopy/geopy) - Python Geocoding Toolbox. -## HTML Manipulation - -_Libraries for working with HTML and XML._ - -- [beautifulsoup](https://www.crummy.com/software/BeautifulSoup/bs4/doc/) - Providing Pythonic idioms for iterating, searching, and modifying HTML or XML. -- [cssutils](https://pypi.org/project/cssutils/) - A CSS library for Python. -- [html5lib](https://github.com/html5lib/html5lib-python) - A standards-compliant library for parsing and serializing HTML documents and fragments. -- [JustHTML](https://github.com/EmilStenstrom/justhtml/) - A pure Python HTML5 parser that just works. -- [lxml](https://lxml.de/) - A very fast, easy-to-use and versatile library for handling HTML and XML. -- [markupsafe](https://github.com/pallets/markupsafe) - Implements a XML/HTML/XHTML Markup safe string for Python. -- [pyquery](https://github.com/gawel/pyquery) - A jQuery-like library for parsing HTML. -- [untangle](https://github.com/stchris/untangle) - Converts XML documents to Python objects for easy access. -- [WeasyPrint](https://weasyprint.org) - A visual rendering engine for HTML and CSS that can export to PDF. -- [xmldataset](https://xmldataset.readthedocs.io/en/latest/) - Simple XML Parsing. -- [xmltodict](https://github.com/martinblech/xmltodict) - Working with XML feel like you are working with JSON. - -## HTTP Clients - -_Libraries for working with HTTP._ - -- [httpx](https://github.com/encode/httpx) - A next generation HTTP client for Python. -- [requests](https://github.com/psf/requests) - HTTP Requests for Humans. -- [treq](https://github.com/twisted/treq) - Python requests like API built on top of Twisted's HTTP client. -- [urllib3](https://github.com/urllib3/urllib3) - A HTTP library with thread-safe connection pooling, file post support, sanity friendly. +## Science -## Hardware +_Libraries for scientific computing. Also see [Python-for-Scientists](https://github.com/TomNicholas/Python-for-Scientists)._ -_Libraries for programming with hardware._ +- Core + - [numba](https://github.com/numba/numba) - Python JIT compiler to LLVM aimed at scientific Python. + - [numpy](https://github.com/numpy/numpy) - A fundamental package for scientific computing with Python. + - [scipy](https://github.com/scipy/scipy) - A Python-based ecosystem of open-source software for mathematics, science, and engineering. + - [statsmodels](https://github.com/statsmodels/statsmodels) - Statistical modeling and econometrics in Python. + - [sympy](https://github.com/sympy/sympy) - A Python library for symbolic mathematics. +- Biology and Chemistry + - [biopython](https://github.com/biopython/biopython) - Biopython is a set of freely available tools for biological computation. + - [cclib](https://github.com/cclib/cclib) - A library for parsing and interpreting the results of computational chemistry packages. + - [openbabel](https://github.com/openbabel/openbabel) - A chemical toolbox designed to speak the many languages of chemical data. + - [rdkit](https://github.com/rdkit/rdkit) - Cheminformatics and Machine Learning Software. +- Physics and Engineering + - [astropy](https://github.com/astropy/astropy) - A community Python library for Astronomy. + - [obspy](https://github.com/obspy/obspy) - A Python toolbox for seismology. + - [pydy](https://github.com/pydy/pydy) - Short for Python Dynamics, used to assist with workflow in the modeling of dynamic motion. + - [PythonRobotics](https://github.com/AtsushiSakai/PythonRobotics) - This is a compilation of various robotics algorithms with visualizations. +- Simulation and Modeling + - [pathsim](https://github.com/pathsim/pathsim) - A block-based system modeling and simulation framework with a browser-based visual editor. + - [pymc](https://github.com/pymc-devs/pymc) - Probabilistic programming and Bayesian modeling in Python. + - [simpy](https://gitlab.com/team-simpy/simpy) - A process-based discrete-event simulation framework. +- Other + - [colour](https://github.com/colour-science/colour) - Implementing a comprehensive number of colour theory transformations and algorithms. + - [manim](https://github.com/ManimCommunity/manim) - An animation engine for explanatory math videos. + - [networkx](https://github.com/networkx/networkx) - A high-productivity software for complex networks. + - [shapely](https://github.com/shapely/shapely) - Manipulation and analysis of geometric objects in the Cartesian plane. -- [bleak](https://github.com/hbldh/bleak) - A cross platform Bluetooth Low Energy Client for Python using asyncio. -- [mouse](https://github.com/boppreh/mouse) - Hook and simulate global mouse events on Windows and Linux. -- [pynput](https://github.com/moses-palmer/pynput) - A library to control and monitor input devices. -- [scapy](https://github.com/secdev/scapy) - A brilliant packet manipulation library. +## Quantum Computing -## Image Processing +_Libraries for quantum computing._ -_Libraries for manipulating images._ +- [Cirq](https://github.com/quantumlib/Cirq) — A Google-developed framework focused on hardware-aware quantum circuit design for NISQ devices. +- [pennylane](https://github.com/PennyLaneAI/pennylane) — A hybrid quantum-classical machine learning library with automatic differentiation support. +- [qiskit](https://github.com/Qiskit/qiskit) — An IBM-backed quantum SDK for building, simulating, and running circuits on real quantum hardware. +- [qutip](https://github.com/qutip/qutip) - Quantum Toolbox in Python. -- [pillow](https://github.com/python-pillow/Pillow) - Pillow is the friendly [PIL](http://www.pythonware.com/products/pil/) fork. -- [pymatting](https://github.com/pymatting/pymatting) - A library for alpha matting. -- [python-barcode](https://github.com/WhyNotHugo/python-barcode) - Create barcodes in Python with no extra dependencies. -- [python-qrcode](https://github.com/lincolnloop/python-qrcode) - A pure Python QR Code generator. -- [pyvips](https://github.com/libvips/pyvips) - A fast image processing library with low memory needs. -- [quads](https://github.com/fogleman/Quads) - Computer art based on quadtrees. -- [scikit-image](https://scikit-image.org/) - A Python library for (scientific) image processing. -- [thumbor](https://github.com/thumbor/thumbor) - A smart imaging service. It enables on-demand crop, re-sizing and flipping of images. -- [wand](https://github.com/emcconville/wand) - Python bindings for [MagickWand](http://www.imagemagick.org/script/magick-wand.php), C API for ImageMagick. +**Developer Tools** -## Implementations +## Algorithms and Design Patterns -_Implementations of Python._ +_Python implementation of data structures, algorithms and design patterns. Also see [awesome-algorithms](https://github.com/tayllan/awesome-algorithms)._ -- [clpython](https://github.com/metawilm/cl-python) - Implementation of the Python programming language written in Common Lisp. -- [cpython](https://github.com/python/cpython) - Default, most widely used implementation of the Python programming language written in C. -- [cython](https://github.com/cython/cython) - Optimizing Static Compiler for Python. -- [ironpython](https://github.com/IronLanguages/ironpython3) - Implementation of the Python programming language written in C#. -- [micropython](https://github.com/micropython/micropython) - A lean and efficient Python programming language implementation. -- [numba](https://github.com/numba/numba) - Python JIT compiler to LLVM aimed at scientific Python. -- [peachpy](https://github.com/Maratyszcza/PeachPy) - x86-64 assembler embedded in Python. -- [pypy](https://foss.heptapod.net/pypy/pypy) - A very fast and compliant implementation of the Python language. -- [pyston](https://github.com/pyston/pyston/) - A Python implementation using JIT techniques. +- Algorithms + - [algorithms](https://github.com/keon/algorithms) - Minimal examples of data structures and algorithms. + - [sortedcontainers](https://github.com/grantjenks/python-sortedcontainers) - Fast and pure-Python implementation of sorted collections. + - [thealgorithms](https://github.com/TheAlgorithms/Python) - All Algorithms implemented in Python. +- Design Patterns + - [python-cqrs](https://github.com/pypatterns/python-cqrs) - Event-Driven Architecture Framework with CQRS/CQS, Transaction Outbox, Saga orchestration. + - [python-patterns](https://github.com/faif/python-patterns) - A collection of design patterns in Python. + - [transitions](https://github.com/pytransitions/transitions) - A lightweight, object-oriented finite state machine implementation. ## Interactive Interpreter _Interactive Python interpreters (REPL)._ -- [bpython](https://github.com/bpython/bpython) - A fancy interface to the Python interpreter. -- [Jupyter Notebook (IPython)](https://jupyter.org) - A rich toolkit to help you make the most out of using Python interactively. +- [jupyter](https://github.com/jupyter/notebook) - A rich toolkit to help you make the most out of using Python interactively. - [awesome-jupyter](https://github.com/markusschanta/awesome-jupyter) - [marimo](https://github.com/marimo-team/marimo) - Transform data and train models, feels like a next-gen notebook, stored as Git-friendly Python. -- [ptpython](https://github.com/jonathanslenders/ptpython) - Advanced Python REPL built on top of the [python-prompt-toolkit](https://github.com/jonathanslenders/python-prompt-toolkit). +- [ptpython](https://github.com/prompt-toolkit/ptpython) - Advanced Python REPL built on top of the [python-prompt-toolkit](https://github.com/prompt-toolkit/python-prompt-toolkit). -## Internationalization +## Code Analysis -_Libraries for working with i18n._ +_Tools of static analysis, linters and code quality checkers. Also see [awesome-static-analysis](https://github.com/analysis-tools-dev/static-analysis)._ -- [Babel](https://babel.pocoo.org/en/latest/) - An internationalization library for Python. +- Code Analysis + - [code2flow](https://github.com/scottrogowski/code2flow) - Turn your Python and JavaScript code into DOT flowcharts. + - [prospector](https://github.com/prospector-dev/prospector) - A tool to analyze Python code. + - [vulture](https://github.com/jendrikseipp/vulture) - A tool for finding and analyzing dead Python code. +- Code Linters + - [bandit](https://github.com/PyCQA/bandit) - A tool designed to find common security issues in Python code. + - [flake8](https://github.com/PyCQA/flake8) - A wrapper around `pycodestyle`, `pyflakes` and McCabe. + - [awesome-flake8-extensions](https://github.com/DmytroLitvinov/awesome-flake8-extensions) + - [pylint](https://github.com/pylint-dev/pylint) - A fully customizable source code analyzer. + - [ruff](https://github.com/astral-sh/ruff) - An extremely fast Python linter and code formatter. +- Code Formatters + - [black](https://github.com/psf/black) - The uncompromising Python code formatter. + - [isort](https://github.com/PyCQA/isort) - A Python utility / library to sort imports. +- Static Type Checkers, also see [awesome-python-typing](https://github.com/typeddjango/awesome-python-typing) + - [mypy](https://github.com/python/mypy) - Check variable types during compile time. + - [pyre-check](https://github.com/facebook/pyre-check) - Performant type checking. + - [ty](https://github.com/astral-sh/ty) - An extremely fast Python type checker and language server. + - [typeshed](https://github.com/python/typeshed) - Collection of library stubs for Python, with static types. +- Refactoring + - [rope](https://github.com/python-rope/rope) - Rope is a python refactoring library. +- Static Type Annotations Generators + - [monkeytype](https://github.com/Instagram/MonkeyType) - A system for Python that generates static type annotations by collecting runtime types. + - [pytype](https://github.com/google/pytype) - Pytype checks and infers types for Python code - without requiring type annotations. -## Job Scheduler +## Testing -_Libraries for scheduling jobs._ +_Libraries for testing codebases and generating test data._ -- [Airflow](https://airflow.apache.org/) - Airflow is a platform to programmatically author, schedule and monitor workflows. -- [APScheduler](http://apscheduler.readthedocs.io/en/latest/) - A light but powerful in-process task scheduler that lets you schedule functions. -- [django-schedule](https://github.com/thauber/django-schedule) - A calendaring app for Django. -- [doit](http://pydoit.org/) - A task runner and build tool. -- [Joblib](https://joblib.readthedocs.io/) - A set of tools to provide lightweight pipelining in Python. -- [Plan](https://github.com/fengsp/plan) - Writing crontab file in Python like a charm. -- [Prefect](https://github.com/PrefectHQ/prefect) - A modern workflow orchestration framework that makes it easy to build, schedule and monitor robust data pipelines. -- [schedule](https://github.com/dbader/schedule) - Python job scheduling for humans. -- [Spiff](https://github.com/knipknap/SpiffWorkflow) - A powerful workflow engine implemented in pure Python. -- [TaskFlow](https://docs.openstack.org/developer/taskflow/) - A Python library that helps to make task execution easy, consistent and reliable. +- Testing Frameworks + - [hypothesis](https://github.com/HypothesisWorks/hypothesis) - Hypothesis is an advanced Quickcheck style property based testing library. + - [pytest](https://github.com/pytest-dev/pytest) - A mature full-featured Python testing tool. + - [robotframework](https://github.com/robotframework/robotframework) - A generic test automation framework. + - [scanapi](https://github.com/scanapi/scanapi) - Automated Testing and Documentation for your REST API. + - [unittest](https://docs.python.org/3/library/unittest.html) - (Python standard library) Unit testing framework. +- Test Runners + - [nox](https://github.com/wntrblm/nox) - Flexible test automation for Python. + - [tox](https://github.com/tox-dev/tox) - Auto builds and tests distributions in multiple Python versions +- GUI / Web Testing + - [locust](https://github.com/locustio/locust) - Scalable user load testing tool written in Python. + - [playwright](https://github.com/microsoft/playwright-python) - Python version of the Playwright testing and automation library. + - [pyautogui](https://github.com/asweigart/pyautogui) - PyAutoGUI is a cross-platform GUI automation Python module for human beings. + - [schemathesis](https://github.com/schemathesis/schemathesis) - A tool for automatic property-based testing of web applications built with Open API / Swagger specifications. + - [selenium](https://github.com/SeleniumHQ/selenium) - Python bindings for [Selenium](https://selenium.dev/) [WebDriver](https://selenium.dev/documentation/webdriver/). +- Mock + - [freezegun](https://github.com/spulec/freezegun) - Travel through time by mocking the datetime module. + - [mock](https://docs.python.org/3/library/unittest.mock.html) - (Python standard library) A mocking and patching library. + - [mocket](https://github.com/mindflayer/python-mocket) - A socket mock framework with gevent/asyncio/SSL support. + - [responses](https://github.com/getsentry/responses) - A utility library for mocking out the requests Python library. + - [vcrpy](https://github.com/kevin1024/vcrpy) - Record and replay HTTP interactions on your tests. +- Object Factories + - [factory_boy](https://github.com/FactoryBoy/factory_boy) - A test fixtures replacement for Python. + - [polyfactory](https://github.com/litestar-org/polyfactory) - mock data generation library with support to classes (continuation of `pydantic-factories`) +- Code Coverage + - [coverage](https://github.com/coveragepy/coveragepy) - Code coverage measurement. +- Fake Data + - [faker](https://github.com/joke2k/faker) - A Python package that generates fake data. + - [mimesis](https://github.com/lk-geimfari/mimesis) - is a Python library that help you generate fake data. -## Logging +## Debugging Tools -_Libraries for generating and working with logs._ +_Libraries for debugging code._ -- [logbook](http://logbook.readthedocs.io/en/stable/) - Logging replacement for Python. -- [logging](https://docs.python.org/3/library/logging.html) - (Python standard library) Logging facility for Python. -- [loguru](https://github.com/Delgan/loguru) - Library which aims to bring enjoyable logging in Python. -- [sentry-python](https://github.com/getsentry/sentry-python) - Sentry SDK for Python. -- [structlog](https://www.structlog.org/en/stable/) - Structured logging made easy. +- pdb-like Debugger + - [ipdb](https://github.com/gotcha/ipdb) - IPython-enabled [pdb](https://docs.python.org/3/library/pdb.html). + - [pudb](https://github.com/inducer/pudb) - A full-screen, console-based Python debugger. +- Tracing + - [manhole](https://github.com/ionelmc/python-manhole) - Debugging UNIX socket connections and present the stacktraces for all threads and an interactive prompt. + - [python-hunter](https://github.com/ionelmc/python-hunter) - A flexible code tracing toolkit. +- Profiler + - [py-spy](https://github.com/benfred/py-spy) - A sampling profiler for Python programs. Written in Rust. + - [scalene](https://github.com/plasma-umass/scalene) - A high-performance, high-precision CPU, GPU, and memory profiler for Python. +- Others + - [django-debug-toolbar](https://github.com/django-commons/django-debug-toolbar) - Display various debug information for Django. + - [flask-debugtoolbar](https://github.com/pallets-eco/flask-debugtoolbar) - A port of the django-debug-toolbar to flask. + - [icecream](https://github.com/gruns/icecream) - Inspect variables, expressions, and program execution with a single, simple function call. + - [memory_graph](https://github.com/bterwijn/memory_graph) - Visualize Python data at runtime to debug references, mutability, and aliasing. -## Machine Learning +## Build Tools -_Libraries for Machine Learning. Also see [awesome-machine-learning](https://github.com/josephmisiti/awesome-machine-learning#python)._ +_Compile software from source code._ -- [Agno](https://github.com/agno-agi/agno) - Open-source Python library for building AI agents and agentic systems. -- [diffusers](https://github.com/huggingface/diffusers) - A library that provides pretrained diffusion models for generating and editing images, audio, and video. -- [gym](https://github.com/openai/gym) - A toolkit for developing and comparing reinforcement learning algorithms. -- [Feature-engine](https://github.com/feature-engine/feature_engine) - sklearn compatible API with the widest toolset for feature engineering and selection. -- [H2O](https://github.com/h2oai/h2o-3) - Open Source Fast Scalable Machine Learning Platform. -- [Instructor](https://github.com/567-labs/instructor) - A library for extracting structured data from LLMs, powered by Pydantic. -- [LangChain](https://github.com/langchain-ai/langchain) - Building applications with LLMs through composability. -- [LlamaIndex](https://github.com/run-llama/llama_index) - A data framework for your LLM application. -- [Metrics](https://github.com/benhamner/Metrics) - Machine learning evaluation metrics. -- [MindsDB](https://github.com/mindsdb/mindsdb) - MindsDB is an open source AI layer for existing databases that allows you to effortlessly develop, train and deploy state-of-the-art machine learning models using standard queries. -- [pgmpy](https://github.com/pgmpy/pgmpy) - A Python library for probabilistic graphical models and Bayesian networks. -- [PraisonAI](https://github.com/MervinPraison/PraisonAI) - Production-ready Multi-AI Agents framework with self-reflection, 100+ LLM support, MCP integration, and agentic workflows. -- [pydantic-ai](https://github.com/pydantic/pydantic-ai) - A Python agent framework for building generative AI applications with structured schemas. -- [RAGFlow](https://github.com/infiniflow/ragflow) - An open-source RAG engine for document understanding and question answering with LLMs. -- [rasa](https://github.com/RasaHQ/rasa) - An open-source machine learning framework for automated text and voice-based conversations. -- [scikit-learn](http://scikit-learn.org/) - The most popular Python library for Machine Learning with extensive documentation and community support. -- [Spark ML](http://spark.apache.org/docs/latest/ml-guide.html) - [Apache Spark](http://spark.apache.org/)'s scalable Machine Learning library for distributed computing. -- [Transformers](https://github.com/huggingface/transformers) - A framework that lets you easily use pretrained transformer models for NLP, vision, and audio tasks. -- [xgboost](https://github.com/dmlc/xgboost) - A scalable, portable, and distributed gradient boosting library. +- [bitbake](https://github.com/openembedded/bitbake) - A make-like build tool for embedded Linux. +- [invoke](https://github.com/pyinvoke/invoke) - A tool for managing shell-oriented subprocesses and organizing executable Python code into CLI-invokable tasks. +- [platformio](https://github.com/platformio/platformio-core) - A console tool to build code with different development platforms. +- [pybuilder](https://github.com/pybuilder/pybuilder) - A continuous build tool written in pure Python. +- [doit](https://github.com/pydoit/doit) - A task runner and build tool. +- [scons](https://github.com/SCons/scons) - A software construction tool. -## Microsoft Windows +## Documentation -_Python programming on Microsoft Windows._ +_Libraries for generating project documentation._ -- [PythonNet](https://github.com/pythonnet/pythonnet) - Python Integration with the .NET Common Language Runtime (CLR). -- [PyWin32](https://github.com/mhammond/pywin32) - Python Extensions for Windows. -- [WinPython](https://winpython.github.io/) - Portable development environment for Windows 10/11. +- [sphinx](https://github.com/sphinx-doc/sphinx/) - Python Documentation generator. + - [awesome-sphinxdoc](https://github.com/ygzgxyz/awesome-sphinxdoc) +- [diagrams](https://github.com/mingrammer/diagrams) - Diagram as Code. +- [mkdocs](https://github.com/mkdocs/mkdocs/) - Markdown friendly documentation generator. +- [pdoc](https://github.com/mitmproxy/pdoc) - Epydoc replacement to auto generate API documentation for Python libraries. -## Miscellaneous +## Editor Plugins and IDEs -_Useful libraries or tools that don't fit in the categories above._ +- [elpy](https://github.com/jorgenschaefer/elpy) - Emacs Python Development Environment. +- [jedi-vim](https://github.com/davidhalter/jedi-vim) - Vim bindings for the Jedi auto-completion library for Python. +- [PyCharm](https://www.jetbrains.com/pycharm/) - Commercial Python IDE by JetBrains. Has free community edition available. +- [Python for VSCode](https://marketplace.visualstudio.com/items?itemName=ms-python.python) - The official VSCode extension with rich support for Python. +- [python-mode](https://github.com/python-mode/python-mode) - An all in one plugin for turning Vim into a Python IDE. +- [spyder](https://github.com/spyder-ide/spyder) - Open Source Python IDE. +- [YouCompleteMe](https://github.com/ycm-core/YouCompleteMe) - Includes [Jedi](https://github.com/davidhalter/jedi)-based completion engine for Python. -- [blinker](https://github.com/jek/blinker) - A fast Python in-process signal/event dispatching system. -- [boltons](https://github.com/mahmoud/boltons) - A set of pure-Python utilities. -- [itsdangerous](https://github.com/pallets/itsdangerous) - Various helpers to pass trusted data to untrusted environments. -- [pluginbase](https://github.com/mitsuhiko/pluginbase) - A simple but flexible plugin system for Python. -- [tryton](http://www.tryton.org/) - A general-purpose business framework. +**DevOps** -## Natural Language Processing +## DevOps Tools -_Libraries for working with human languages._ +_Software and libraries for DevOps._ -- General - - [gensim](https://github.com/RaRe-Technologies/gensim) - Topic Modeling for Humans. - - [langid.py](https://github.com/saffsd/langid.py) - Stand-alone language identification system. - - [nltk](https://www.nltk.org/) - A leading platform for building Python programs to work with human language data. - - [pattern](https://github.com/clips/pattern) - A web mining module. - - [polyglot](https://github.com/aboSamoor/polyglot) - Natural language pipeline supporting hundreds of languages. - - [spacy](https://spacy.io/) - A library for industrial-strength natural language processing in Python and Cython. - - [Stanza](https://github.com/stanfordnlp/stanza) - The Stanford NLP Group's official Python library, supporting 60+ languages. -- Chinese - - [funNLP](https://github.com/fighting41love/funNLP) - A collection of tools and datasets for Chinese NLP. - - [jieba](https://github.com/fxsjy/jieba) - The most popular Chinese text segmentation library. - - [pkuseg-python](https://github.com/lancopku/pkuseg-python) - A toolkit for Chinese word segmentation in various domains. - - [snownlp](https://github.com/isnowfy/snownlp) - A library for processing Chinese text. +- Cloud Providers + - [awscli](https://github.com/aws/aws-cli) - Universal Command Line Interface for Amazon Web Services. + - [boto3](https://github.com/boto/boto3) - Python interface to Amazon Web Services. +- Configuration Management + - [ansible](https://github.com/ansible/ansible) - A radically simple IT automation platform. + - [cloudinit](https://github.com/canonical/cloud-init) - A multi-distribution package that handles early initialization of a cloud instance. + - [openstack](https://www.openstack.org/) - Open source software for building private and public clouds. + - [pyinfra](https://github.com/pyinfra-dev/pyinfra) - A versatile CLI tools and python libraries to automate infrastructure. + - [saltstack](https://github.com/saltstack/salt) - Infrastructure automation and management system. +- Deployment + - [chalice](https://github.com/aws/chalice) - A Python serverless microframework for AWS. + - [fabric](https://github.com/fabric/fabric) - A simple, Pythonic tool for remote execution and deployment. +- Monitoring and Processes + - [psutil](https://github.com/giampaolo/psutil) - A cross-platform process and system utilities module. + - [sentry-python](https://github.com/getsentry/sentry-python) - Sentry SDK for Python. + - [sh](https://github.com/amoffat/sh) - A full-fledged subprocess replacement for Python. + - [supervisor](https://github.com/Supervisor/supervisor) - Supervisor process control system for UNIX. +- Other + - [borg](https://github.com/borgbackup/borg) - A deduplicating archiver with compression and encryption. + - [chaostoolkit](https://github.com/chaostoolkit/chaostoolkit) - A Chaos Engineering toolkit & Orchestration for Developers. + - [pre-commit](https://github.com/pre-commit/pre-commit) - A framework for managing and maintaining multi-language pre-commit hooks. -## Network Virtualization +## Distributed Computing -_Tools and libraries for Virtual Networking and SDN (Software Defined Networking)._ +_Frameworks and libraries for Distributed Computing._ -- [mininet](https://github.com/mininet/mininet) - A popular network emulator and API written in Python. -- [napalm](https://github.com/napalm-automation/napalm) - Cross-vendor API to manipulate network devices. -- [pox](https://github.com/noxrepo/pox) - A Python-based SDN control applications, such as OpenFlow SDN controllers. +- Batch Processing + - [dask](https://github.com/dask/dask) - A flexible parallel computing library for analytic computing. + - [luigi](https://github.com/spotify/luigi) - A module that helps you build complex pipelines of batch jobs. + - [mpi4py](https://github.com/mpi4py/mpi4py) - Python bindings for MPI. + - [pyspark](https://github.com/apache/spark) - [Apache Spark](https://spark.apache.org/) Python API. + - [joblib](https://github.com/joblib/joblib) - A set of tools to provide lightweight pipelining in Python. + - [ray](https://github.com/ray-project/ray/) - A system for parallel and distributed Python that unifies the machine learning ecosystem. -## ORM +## Task Queues -_Libraries that implement Object-Relational Mapping or data mapping techniques._ +_Libraries for working with task queues._ -- Relational Databases - - [Django Models](https://docs.djangoproject.com/en/dev/topics/db/models/) - The Django ORM. - - [SQLAlchemy](https://www.sqlalchemy.org/) - The Python SQL Toolkit and Object Relational Mapper. - - [awesome-sqlalchemy](https://github.com/dahlia/awesome-sqlalchemy) - - [dataset](https://github.com/pudo/dataset) - Store Python dicts in a database - works with SQLite, MySQL, and PostgreSQL. - - [peewee](https://github.com/coleifer/peewee) - A small, expressive ORM. - - [pony](https://github.com/ponyorm/pony/) - ORM that provides a generator-oriented interface to SQL. - - [pydal](https://github.com/web2py/pydal/) - A pure Python Database Abstraction Layer. - - [SQLModel](https://sqlmodel.tiangolo.com/) - SQLModel is based on Python type annotations, and powered by Pydantic and SQLAlchemy. -- NoSQL Databases - - [mongoengine](https://github.com/MongoEngine/mongoengine) - A Python Object-Document-Mapper for working with MongoDB. - - [ODMantic](https://github.com/art049/odmantic) - Sync and Async ODM for MongoDB built on top of Pydantic for model definition and validation. - - [PynamoDB](https://github.com/pynamodb/PynamoDB) - A Pythonic interface for [Amazon DynamoDB](https://aws.amazon.com/dynamodb/). +- [celery](https://github.com/celery/celery) - An asynchronous task queue/job queue based on distributed message passing. + - [flower](https://github.com/mher/flower) - Real-time monitor and web admin for Celery. +- [dramatiq](https://github.com/Bogdanp/dramatiq) - A fast and reliable background task processing library for Python 3. +- [huey](https://github.com/coleifer/huey) - Little multi-threaded task queue. +- [rq](https://github.com/rq/rq) - Simple job queues for Python. -## Package Management +## Job Schedulers -_Libraries for package and dependency management._ +_Libraries for scheduling jobs._ -- [pip](https://pip.pypa.io/en/stable/) - The package installer for Python. - - [pip-tools](https://github.com/jazzband/pip-tools) - A set of tools to keep your pinned Python dependencies fresh. -- [conda](https://github.com/conda/conda/) - Cross-platform, Python-agnostic binary package manager. -- [hatch](https://github.com/pypa/hatch) - Modern, extensible Python project management. -- [poetry](https://github.com/sdispater/poetry) - Python dependency management and packaging made easy. -- [uv](https://github.com/astral-sh/uv) - An extremely fast Python package and project manager, written in Rust. +- [airflow](https://github.com/apache/airflow) - Airflow is a platform to programmatically author, schedule and monitor workflows. +- [apscheduler](https://github.com/agronholm/apscheduler) - A light but powerful in-process task scheduler that lets you schedule functions. +- [dagster](https://github.com/dagster-io/dagster) - An orchestration platform for the development, production, and observation of data assets. +- [prefect](https://github.com/PrefectHQ/prefect) - A modern workflow orchestration framework that makes it easy to build, schedule and monitor robust data pipelines. +- [schedule](https://github.com/dbader/schedule) - Python job scheduling for humans. +- [SpiffWorkflow](https://github.com/sartography/SpiffWorkflow) - A powerful workflow engine implemented in pure Python. +- [taskflow](https://docs.openstack.org/developer/taskflow/) - A Python library that helps to make task execution easy, consistent and reliable. -## Package Repositories +## Logging -_Local PyPI repository server and proxies._ +_Libraries for generating and working with logs._ -- [bandersnatch](https://github.com/pypa/bandersnatch/) - PyPI mirroring tool provided by Python Packaging Authority (PyPA). -- [devpi](https://github.com/devpi/devpi) - PyPI server and packaging/testing/release tool. -- [warehouse](https://github.com/pypa/warehouse) - Next generation Python Package Repository (PyPI). +- [logging](https://docs.python.org/3/library/logging.html) - (Python standard library) Logging facility for Python. +- [loguru](https://github.com/Delgan/loguru) - Library which aims to bring enjoyable logging in Python. +- [structlog](https://github.com/hynek/structlog) - Structured logging made easy. -## Penetration Testing +## Network Virtualization -_Frameworks and tools for penetration testing._ +_Tools and libraries for Virtual Networking and SDN (Software Defined Networking)._ -- [fsociety](https://github.com/Manisso/fsociety) - A Penetration testing framework. -- [setoolkit](https://github.com/trustedsec/social-engineer-toolkit) - A toolkit for social engineering. -- [sherlock](https://github.com/sherlock-project/sherlock) - Hunt down social media accounts by username across social networks. -- [sqlmap](https://github.com/sqlmapproject/sqlmap) - Automatic SQL injection and database takeover tool. +- [mininet](https://github.com/mininet/mininet) - A popular network emulator and API written in Python. +- [napalm](https://github.com/napalm-automation/napalm) - Cross-vendor API to manipulate network devices. +- [scapy](https://github.com/secdev/scapy) - A brilliant packet manipulation library. + +## RPC Servers + +_RPC-compatible servers._ -## Permissions +- [grpcio](https://github.com/grpc/grpc) - HTTP/2-based RPC framework with Python bindings, built by Google. +- [rpyc](https://github.com/tomerfiliba-org/rpyc) (Remote Python Call) - A transparent and symmetric RPC library for Python -_Libraries that allow or deny users access to data or functionality._ +**CLI & GUI** -- [django-guardian](https://github.com/django-guardian/django-guardian) - Implementation of per object permissions for Django 1.2+ -- [django-rules](https://github.com/dfunckt/django-rules) - A tiny but powerful app providing object-level permissions to Django, without requiring a database. +## Command-line Interface Development -## Processes +_Libraries for building command-line applications._ -_Libraries for starting and communicating with OS processes._ +- Command-line Application Development + - [argparse](https://docs.python.org/3/library/argparse.html) - (Python standard library) Command-line option and argument parsing. + - [cement](https://github.com/datafolklabs/cement) - CLI Application Framework for Python. + - [click](https://github.com/pallets/click/) - A package for creating beautiful command line interfaces in a composable way. + - [python-fire](https://github.com/google/python-fire) - A library for creating command line interfaces from absolutely any Python object. + - [python-prompt-toolkit](https://github.com/prompt-toolkit/python-prompt-toolkit) - A library for building powerful interactive command lines. + - [typer](https://github.com/fastapi/typer) - Modern CLI framework that uses Python type hints. Built on Click and Pydantic. +- Terminal Rendering + - [alive-progress](https://github.com/rsalmei/alive-progress) - A new kind of Progress Bar, with real-time throughput, eta and very cool animations. + - [asciimatics](https://github.com/peterbrittain/asciimatics) - A package to create full-screen text UIs (from interactive forms to ASCII animations). + - [colorama](https://github.com/tartley/colorama) - Cross-platform colored terminal text. + - [rich](https://github.com/Textualize/rich) - Python library for rich text and beautiful formatting in the terminal. Also provides a great `RichHandler` log handler. + - [textual](https://github.com/Textualize/textual) - A framework for building interactive user interfaces that run in the terminal and the browser. + - [tqdm](https://github.com/tqdm/tqdm) - Fast, extensible progress bar for loops and CLI. -- [delegator.py](https://github.com/amitt001/delegator.py) - [Subprocesses](https://docs.python.org/3/library/subprocess.html) for Humans 2.0. -- [sarge](https://sarge.readthedocs.io/en/latest/) - Yet another wrapper for subprocess. -- [sh](https://github.com/amoffat/sh) - A full-fledged subprocess replacement for Python. +## Command-line Tools -## Quantum Computing +_Useful CLI-based tools for productivity._ -_Libraries for quantum computing._ +- Productivity Tools + - [cookiecutter](https://github.com/cookiecutter/cookiecutter) - A command-line utility that creates projects from cookiecutters (project templates). + - [copier](https://github.com/copier-org/copier) - A library and command-line utility for rendering projects templates. + - [doitlive](https://github.com/sloria/doitlive) - A tool for live presentations in the terminal. + - [fpp](https://github.com/facebook/PathPicker) - Select files out of bash output. + - [thefuck](https://github.com/nvbn/thefuck) - Correcting your previous console command. + - [tmuxp](https://github.com/tmux-python/tmuxp) - A [tmux](https://github.com/tmux/tmux) session manager. + - [xonsh](https://github.com/xonsh/xonsh/) - A Python-powered shell. Full-featured and cross-platform. + - [yt-dlp](https://github.com/yt-dlp/yt-dlp) - A command-line program to download videos from YouTube and other video sites, a fork of youtube-dl. +- CLI Enhancements + - [httpie](https://github.com/httpie/cli) - A command line HTTP client, a user-friendly cURL replacement. + - [iredis](https://github.com/laixintao/iredis) - Redis CLI with autocompletion and syntax highlighting. + - [litecli](https://github.com/dbcli/litecli) - SQLite CLI with autocompletion and syntax highlighting. + - [mycli](https://github.com/dbcli/mycli) - MySQL CLI with autocompletion and syntax highlighting. + - [pgcli](https://github.com/dbcli/pgcli) - PostgreSQL CLI with autocompletion and syntax highlighting. -- [Cirq](https://github.com/quantumlib/Cirq) — A Google-developed framework focused on hardware-aware quantum circuit design for NISQ devices. -- [PennyLane](https://github.com/PennyLaneAI/pennylane) — A hybrid quantum-classical machine learning library with automatic differentiation support. -- [Qiskit](https://github.com/Qiskit/qiskit) — An IBM-backed quantum SDK for building, simulating, and running circuits on real quantum hardware. -- [QuTiP](http://qutip.org/) - Quantum Toolbox in Python. +## GUI Development -## Recommender Systems +_Libraries for working with graphical user interface applications._ -_Libraries for building recommender systems._ +- Desktop + - [customtkinter](https://github.com/tomschimansky/customtkinter) - A modern and customizable python UI-library based on Tkinter. + - [dearpygui](https://github.com/hoffstadt/DearPyGui) - A Simple GPU accelerated Python GUI framework + - [enaml](https://github.com/nucleic/enaml) - Creating beautiful user-interfaces with Declarative Syntax like QML. + - [kivy](https://github.com/kivy/kivy) - A library for creating NUI applications, running on Windows, Linux, Mac OS X, Android and iOS. + - [pyglet](https://github.com/pyglet/pyglet) - A cross-platform windowing and multimedia library for Python. + - [pygobject](https://github.com/GNOME/pygobject) - Python Bindings for GLib/GObject/GIO/GTK+ (GTK+3). + - [PyQt](https://www.riverbankcomputing.com/static/Docs/PyQt6/) - Python bindings for the [Qt](https://www.qt.io/) cross-platform application and UI framework. + - [pyside](https://github.com/pyside/pyside-setup) - Qt for Python offers the official Python bindings for [Qt](https://www.qt.io/), this is same as PyQt but it's the official binding with different licensing. + - [tkinter](https://docs.python.org/3/library/tkinter.html) - (Python standard library) The standard Python interface to the Tcl/Tk GUI toolkit. + - [toga](https://github.com/beeware/toga) - A Python native, OS native GUI toolkit. + - [wxPython](https://github.com/wxWidgets/Phoenix) - A blending of the wxWidgets C++ class library with the Python. +- Web-based + - [flet](https://github.com/flet-dev/flet) - Cross-platform GUI framework for building modern apps in pure Python. + - [nicegui](https://github.com/zauberzeug/nicegui) - An easy-to-use, Python-based UI framework, which shows up in your web browser. + - [pywebview](https://github.com/r0x0r/pywebview/) - A lightweight cross-platform native wrapper around a webview component. +- Terminal + - [curses](https://docs.python.org/3/library/curses.html) - Built-in wrapper for [ncurses](http://www.gnu.org/software/ncurses/) used to create terminal GUI applications. + - [urwid](https://github.com/urwid/urwid) - A library for creating terminal GUI applications with strong support for widgets, events, rich colors, etc. +- Wrappers + - [gooey](https://github.com/chriskiehl/Gooey) - Turn command line programs into a full GUI application with one line. + +**Text & Documents** -- [annoy](https://github.com/spotify/annoy) - Approximate Nearest Neighbors in C++/Python optimized for memory usage. -- [fastFM](https://github.com/ibayer/fastFM) - A library for Factorization Machines. -- [implicit](https://github.com/benfred/implicit) - A fast Python implementation of collaborative filtering for implicit datasets. -- [lightfm](https://github.com/lyst/lightfm) - A Python implementation of a number of popular recommendation algorithms. -- [spotlight](https://github.com/maciejkula/spotlight) - Deep recommender models using PyTorch. -- [Surprise](https://github.com/NicolasHug/Surprise) - A scikit for building and analyzing recommender systems. -- [tensorrec](https://github.com/jfkirk/tensorrec) - A Recommendation Engine Framework in TensorFlow. +## Text Processing -## Refactoring +_Libraries for parsing and manipulating plain texts._ -_Refactoring tools and libraries for Python._ +- General + - [babel](https://github.com/python-babel/babel) - An internationalization library for Python. + - [chardet](https://github.com/chardet/chardet) - Python 2/3 compatible character encoding detector. + - [difflib](https://docs.python.org/3/library/difflib.html) - (Python standard library) Helpers for computing deltas. + - [ftfy](https://github.com/rspeer/python-ftfy) - Makes Unicode text less broken and more consistent automagically. + - [pangu.py](https://github.com/vinta/pangu.py) - Paranoid text spacing. + - [pyfiglet](https://github.com/pwaller/pyfiglet) - An implementation of figlet written in Python. + - [pypinyin](https://github.com/mozillazg/python-pinyin) - Convert Chinese hanzi (漢字) to pinyin (拼音). + - [python-slugify](https://github.com/un33k/python-slugify) - A Python slugify library that translates unicode to ASCII. + - [textdistance](https://github.com/life4/textdistance) - Compute distance between sequences with 30+ algorithms. + - [unidecode](https://github.com/avian2/unidecode) - ASCII transliterations of Unicode text. +- Unique identifiers + - [hashids](https://github.com/davidaurelio/hashids-python) - Implementation of [hashids](http://hashids.org) in Python. + - [shortuuid](https://github.com/skorokithakis/shortuuid) - A generator library for concise, unambiguous and URL-safe UUIDs. +- Parser + - [pygments](https://github.com/pygments/pygments) - A generic syntax highlighter. + - [pyparsing](https://github.com/pyparsing/pyparsing) - A general purpose framework for generating parsers. + - [python-nameparser](https://github.com/derek73/python-nameparser) - Parsing human names into their individual components. + - [python-phonenumbers](https://github.com/daviddrysdale/python-phonenumbers) - Parsing, formatting, storing and validating international phone numbers. + - [python-user-agents](https://github.com/selwin/python-user-agents) - Browser user agent parser. + - [sqlparse](https://github.com/andialbrecht/sqlparse) - A non-validating SQL parser. -- [Bowler](https://pybowler.io/) - Safe code refactoring for modern Python. -- [Rope](https://github.com/python-rope/rope) - Rope is a python refactoring library. +## HTML Manipulation -## RESTful API +_Libraries for working with HTML and XML._ -_Libraries for building RESTful APIs._ +- [beautifulsoup](https://www.crummy.com/software/BeautifulSoup/bs4/doc/) - Providing Pythonic idioms for iterating, searching, and modifying HTML or XML. +- [cssutils](https://github.com/jaraco/cssutils) - A CSS library for Python. +- [justhtml](https://github.com/EmilStenstrom/justhtml/) - A pure Python HTML5 parser that just works. +- [lxml](https://github.com/lxml/lxml) - A very fast, easy-to-use and versatile library for handling HTML and XML. +- [markupsafe](https://github.com/pallets/markupsafe) - Implements a XML/HTML/XHTML Markup safe string for Python. +- [pyquery](https://github.com/gawel/pyquery) - A jQuery-like library for parsing HTML. +- [xmltodict](https://github.com/martinblech/xmltodict) - Working with XML feel like you are working with JSON. -- Django - - [django-rest-framework](https://github.com/encode/django-rest-framework) - A powerful and flexible toolkit to build web APIs. - - [django-tastypie](https://github.com/django-tastypie/django-tastypie) - Creating delicious APIs for Django apps. -- Flask - - [eve](https://github.com/pyeve/eve) - REST API framework powered by Flask, MongoDB and good intentions. - - [flask-api](https://github.com/flask-api/flask-api) - Browsable Web APIs for Flask. - - [flask-restful](https://github.com/flask-restful/flask-restful) - Quickly building REST APIs for Flask. -- Pyramid - - [cornice](https://github.com/Cornices/cornice) - A RESTful framework for Pyramid. -- Framework agnostic - - [falcon](https://github.com/falconry/falcon) - A high-performance framework for building cloud APIs and web app backends. - - [fastapi](https://github.com/tiangolo/fastapi) - A modern, fast, web framework for building APIs with Python 3.6+ based on standard Python type hints. - - [hug](https://github.com/hugapi/hug) - A Python 3 framework for cleanly exposing APIs. - - [sandman2](https://github.com/jeffknupp/sandman2) - Automated REST APIs for existing database-driven systems. - - [sanic](https://github.com/sanic-org/sanic) - A Python 3.6+ web server and web framework that's written to go fast. +## File Format Processing -## Robotics +_Libraries for parsing and manipulating specific text formats._ -_Libraries for robotics._ +- General + - [docling](https://github.com/docling-project/docling) - Library for converting documents into structured data. + - [kreuzberg](https://github.com/kreuzberg-dev/kreuzberg) - High-performance document extraction library with a Rust core, supporting 62+ formats including PDF, Office, images with OCR, HTML, email, and archives. + - [pyelftools](https://github.com/eliben/pyelftools) - Parsing and analyzing ELF files and DWARF debugging information. + - [tablib](https://github.com/jazzband/tablib) - A module for Tabular Datasets in XLS, CSV, JSON, YAML. +- Office + - [docxtpl](https://github.com/elapouya/python-docx-template) - Editing a docx document by jinja2 template + - [openpyxl](https://openpyxl.readthedocs.io/en/stable/) - A library for reading and writing Excel 2010 xlsx/xlsm/xltx/xltm files. + - [pyexcel](https://github.com/pyexcel/pyexcel) - Providing one API for reading, manipulating and writing csv, ods, xls, xlsx and xlsm files. + - [python-docx](https://github.com/python-openxml/python-docx) - Reads, queries and modifies Microsoft Word 2007/2008 docx files. + - [python-pptx](https://github.com/scanny/python-pptx) - Python library for creating and updating PowerPoint (.pptx) files. + - [xlsxwriter](https://github.com/jmcnamara/XlsxWriter) - A Python module for creating Excel .xlsx files. + - [xlwings](https://github.com/ZoomerAnalytics/xlwings) - A BSD-licensed library that makes it easy to call Python from Excel and vice versa. +- PDF + - [pdf_oxide](https://github.com/yfedoseev/pdf_oxide) - A fast PDF library for text extraction, image extraction, and markdown conversion, powered by Rust. + - [pdfminer.six](https://github.com/pdfminer/pdfminer.six) - Pdfminer.six is a community maintained fork of the original PDFMiner. + - [pikepdf](https://github.com/pikepdf/pikepdf) - A powerful library for reading and editing PDF files, based on qpdf. + - [pypdf](https://github.com/py-pdf/pypdf) - A library capable of splitting, merging, cropping, and transforming PDF pages. + - [reportlab](https://www.reportlab.com/opensource/) - Allowing Rapid creation of rich PDF documents. + - [weasyprint](https://github.com/Kozea/WeasyPrint) - A visual rendering engine for HTML and CSS that can export to PDF. +- Markdown + - [markdown-it-py](https://github.com/executablebooks/markdown-it-py) - Markdown parser with 100% CommonMark support, extensions, and syntax plugins. + - [markdown](https://github.com/waylan/Python-Markdown) - A Python implementation of John Gruber’s Markdown. + - [markitdown](https://github.com/microsoft/markitdown) - Python tool for converting files and office documents to Markdown. + - [mistune](https://github.com/lepture/mistune) - Fastest and full featured pure Python parsers of Markdown. +- Data Formats + - [csvkit](https://github.com/wireservice/csvkit) - Utilities for converting to and working with CSV. + - [pyyaml](https://github.com/yaml/pyyaml) - YAML implementations for Python. + - [tomllib](https://docs.python.org/3/library/tomllib.html) - (Python standard library) Parse TOML files. -- [PythonRobotics](https://github.com/AtsushiSakai/PythonRobotics) - This is a compilation of various robotics algorithms with visualizations. +## File Manipulation -## RPC Servers +_Libraries for file manipulation._ -_RPC-compatible servers._ +- [mimetypes](https://docs.python.org/3/library/mimetypes.html) - (Python standard library) Map filenames to MIME types. +- [pathlib](https://docs.python.org/3/library/pathlib.html) - (Python standard library) A cross-platform, object-oriented path library. +- [python-magic](https://github.com/ahupp/python-magic) - A Python interface to the libmagic file type identification library. +- [watchdog](https://github.com/gorakhargosh/watchdog) - API and shell utilities to monitor file system events. +- [watchfiles](https://github.com/samuelcolvin/watchfiles) - Simple, modern and fast file watching and code reload in python. -- [RPyC](https://github.com/tomerfiliba/rpyc) (Remote Python Call) - A transparent and symmetric RPC library for Python -- [zeroRPC](https://github.com/0rpc/zerorpc-python) - zerorpc is a flexible RPC implementation based on [ZeroMQ](http://zeromq.org/) and [MessagePack](http://msgpack.org/). +**Media** -## Science +## Image Processing -_Libraries for scientific computing. Also see [Python-for-Scientists](https://github.com/TomNicholas/Python-for-Scientists)._ +_Libraries for manipulating images._ -- [astropy](https://www.astropy.org/) - A community Python library for Astronomy. -- [bcbio-nextgen](https://github.com/chapmanb/bcbio-nextgen) - Providing best-practice pipelines for fully automated high throughput sequencing analysis. -- [bcbb](https://github.com/chapmanb/bcbb) - Collection of useful code related to biological analysis. -- [Biopython](http://biopython.org/wiki/Main_Page) - Biopython is a set of freely available tools for biological computation. -- [cclib](http://cclib.github.io/) - A library for parsing and interpreting the results of computational chemistry packages. -- [Colour](http://colour-science.org/) - Implementing a comprehensive number of colour theory transformations and algorithms. -- [Karate Club](https://github.com/benedekrozemberczki/karateclub) - Unsupervised machine learning toolbox for graph structured data. -- [manim](https://github.com/ManimCommunity/manim) - An animation engine for explanatory math videos. -- [NetworkX](https://networkx.github.io/) - A high-productivity software for complex networks. -- [NIPY](http://nipy.org) - A collection of neuroimaging toolkits. -- [NumPy](http://www.numpy.org/) - A fundamental package for scientific computing with Python. -- [ObsPy](https://github.com/obspy/obspy/wiki/) - A Python toolbox for seismology. -- [Open Babel](https://open-babel.readthedocs.io/) - A chemical toolbox designed to speak the many languages of chemical data. -- [PathSim](https://github.com/pathsim/pathsim) - A block-based system modeling and simulation framework with a browser-based visual editor. -- [PyDy](http://www.pydy.org/) - Short for Python Dynamics, used to assist with workflow in the modeling of dynamic motion. -- [PyMC](https://github.com/pymc-devs/pymc3) - Markov Chain Monte Carlo sampling toolkit. -- [RDKit](http://www.rdkit.org/) - Cheminformatics and Machine Learning Software. -- [SciPy](https://www.scipy.org/) - A Python-based ecosystem of open-source software for mathematics, science, and engineering. -- [shapely](https://github.com/shapely/shapely) - Manipulation and analysis of geometric objects in the Cartesian plane. -- [SimPy](https://gitlab.com/team-simpy/simpy) - A process-based discrete-event simulation framework. -- [statsmodels](https://github.com/statsmodels/statsmodels) - Statistical modeling and econometrics in Python. -- [SymPy](https://github.com/sympy/sympy) - A Python library for symbolic mathematics. -- [Zipline](https://github.com/quantopian/zipline) - A Pythonic algorithmic trading library. +- [pillow](https://github.com/python-pillow/Pillow) - Pillow is the friendly [PIL](http://www.pythonware.com/products/pil/) fork. +- [pymatting](https://github.com/pymatting/pymatting) - A library for alpha matting. +- [python-barcode](https://github.com/WhyNotHugo/python-barcode) - Create barcodes in Python with no extra dependencies. +- [python-qrcode](https://github.com/lincolnloop/python-qrcode) - A pure Python QR Code generator. +- [pyvips](https://github.com/libvips/pyvips) - A fast image processing library with low memory needs. +- [scikit-image](https://github.com/scikit-image/scikit-image) - A Python library for (scientific) image processing. +- [thumbor](https://github.com/thumbor/thumbor) - A smart imaging service. It enables on-demand crop, re-sizing and flipping of images. +- [wand](https://github.com/emcconville/wand) - Python bindings for [MagickWand](http://www.imagemagick.org/script/magick-wand.php), C API for ImageMagick. -## Search +## Audio & Video Processing -_Libraries and software for indexing and performing search queries on data._ +_Libraries for manipulating audio, video, and their metadata._ -- [django-haystack](https://github.com/django-haystack/django-haystack) - Modular search for Django. -- [elasticsearch-dsl-py](https://github.com/elastic/elasticsearch-dsl-py) - The official high-level Python client for Elasticsearch. -- [elasticsearch-py](https://www.elastic.co/guide/en/elasticsearch/client/python-api/current/index.html) - The official low-level Python client for [Elasticsearch](https://www.elastic.co/products/elasticsearch). -- [pysolr](https://github.com/django-haystack/pysolr) - A lightweight Python wrapper for [Apache Solr](https://lucene.apache.org/solr/). -- [whoosh](http://whoosh.readthedocs.io/en/latest/) - A fast, pure Python search engine library. +- Audio + - [gtts](https://github.com/pndurette/gTTS) - Python library and CLI tool for converting text to speech using Google Translate TTS. + - [librosa](https://github.com/librosa/librosa) - Python library for audio and music analysis. + - [matchering](https://github.com/sergree/matchering) - A library for automated reference audio mastering. + - [pydub](https://github.com/jiaaro/pydub) - Manipulate audio with a simple and easy high level interface. +- Video + - [moviepy](https://github.com/Zulko/moviepy) - A module for script-based movie editing with many formats, including animated GIFs. + - [vidgear](https://github.com/abhiTronix/vidgear) - Most Powerful multi-threaded Video Processing framework. +- Metadata + - [beets](https://github.com/beetbox/beets) - A music library manager and [MusicBrainz](https://musicbrainz.org/) tagger. + - [mutagen](https://github.com/quodlibet/mutagen) - A Python module to handle audio metadata. + - [tinytag](https://github.com/devsnd/tinytag) - A library for reading music meta data of MP3, OGG, FLAC and Wave files. -## Serialization +## Game Development -_Libraries for serializing complex data types._ +_Awesome game development libraries._ -- [marshmallow](https://github.com/marshmallow-code/marshmallow) - A lightweight library for converting complex objects to and from simple Python datatypes. -- [orjson](https://github.com/ijl/orjson) - Fast, correct JSON library. -- [pysimdjson](https://github.com/TkTech/pysimdjson) - A Python bindings for [simdjson](https://github.com/lemire/simdjson). -- [python-rapidjson](https://github.com/python-rapidjson/python-rapidjson) - A Python wrapper around [RapidJSON](https://github.com/Tencent/rapidjson). -- [toonify](https://github.com/ScrapeGraphAI/toonify) - A compact, human-readable serialization format that reduces LLM token usage by 30-60% compared to JSON. -- [ultrajson](https://github.com/esnme/ultrajson) - A fast JSON decoder and encoder written in C with Python bindings. +- [arcade](https://github.com/pythonarcade/arcade) - Arcade is a modern Python framework for crafting games with compelling graphics and sound. +- [panda3d](https://github.com/panda3d/panda3d) - 3D game engine developed by Disney. +- [py-sdl2](https://github.com/py-sdl/py-sdl2) - A ctypes based wrapper for the SDL2 library. +- [pygame](https://github.com/pygame/pygame) - Pygame is a set of Python modules designed for writing games. +- [pyopengl](https://github.com/mcfletch/pyopengl) - Python ctypes bindings for OpenGL and it's related APIs. +- [renpy](https://github.com/renpy/renpy) - A Visual Novel engine. -## Serverless Frameworks +**Python Language** -_Frameworks for developing serverless Python code._ +## Implementations -- [python-lambda](https://github.com/nficano/python-lambda) - A toolkit for developing and deploying Python code in AWS Lambda. -- [Zappa](https://github.com/zappa/Zappa) - A tool for deploying WSGI applications on AWS Lambda and API Gateway. +_Implementations of Python._ -## Shell +- [cpython](https://github.com/python/cpython) - Default, most widely used implementation of the Python programming language written in C. +- [cython](https://github.com/cython/cython) - Optimizing Static Compiler for Python. +- [ironpython](https://github.com/IronLanguages/ironpython3) - Implementation of the Python programming language written in C#. +- [micropython](https://github.com/micropython/micropython) - A lean and efficient Python programming language implementation. +- [pypy](https://github.com/pypy/pypy) - A very fast and compliant implementation of the Python language. -_Shells built with Python._ +## Built-in Classes Enhancement -- [xonsh](https://github.com/xonsh/xonsh/) - A Python-powered shell. Full-featured and cross-platform. +_Libraries for enhancing Python built-in classes._ -## Specific Formats Processing +- [attrs](https://github.com/python-attrs/attrs) - Replacement for `__init__`, `__eq__`, `__repr__`, etc. boilerplate in class definitions. +- [bidict](https://github.com/jab/bidict) - Efficient, Pythonic bidirectional map data structures and related functionality. +- [box](https://github.com/cdgriffith/Box) - Python dictionaries with advanced dot notation access. -_Libraries for parsing and manipulating specific text formats._ +## Functional Programming -- General - - [kreuzberg](https://github.com/kreuzberg-dev/kreuzberg) - High-performance document extraction library with a Rust core, supporting 62+ formats including PDF, Office, images with OCR, HTML, email, and archives. - - [tablib](https://github.com/jazzband/tablib) - A module for Tabular Datasets in XLS, CSV, JSON, YAML. -- Office - - [docxtpl](https://github.com/elapouya/python-docx-template) - Editing a docx document by jinja2 template - - [openpyxl](https://openpyxl.readthedocs.io/en/stable/) - A library for reading and writing Excel 2010 xlsx/xlsm/xltx/xltm files. - - [pyexcel](https://github.com/pyexcel/pyexcel) - Providing one API for reading, manipulating and writing csv, ods, xls, xlsx and xlsm files. - - [python-docx](https://github.com/python-openxml/python-docx) - Reads, queries and modifies Microsoft Word 2007/2008 docx files. - - [python-pptx](https://github.com/scanny/python-pptx) - Python library for creating and updating PowerPoint (.pptx) files. - - [XlsxWriter](https://github.com/jmcnamara/XlsxWriter) - A Python module for creating Excel .xlsx files. - - [xlwings](https://github.com/ZoomerAnalytics/xlwings) - A BSD-licensed library that makes it easy to call Python from Excel and vice versa. -- PDF - - [pdf_oxide](https://github.com/yfedoseev/pdf_oxide) - A fast PDF library for text extraction, image extraction, and markdown conversion, powered by Rust. - - [pdfminer.six](https://github.com/pdfminer/pdfminer.six) - Pdfminer.six is a community maintained fork of the original PDFMiner. - - [pikepdf](https://github.com/pikepdf/pikepdf) - A powerful library for reading and editing PDF files, based on qpdf. - - [PyPDF2](https://github.com/mstamy2/PyPDF2) - A library capable of splitting, merging and transforming PDF pages. - - [ReportLab](https://www.reportlab.com/opensource/) - Allowing Rapid creation of rich PDF documents. -- Markdown - - [Jimmy](https://github.com/marph91/jimmy) - Convert your notes to Markdown. - - [Mistune](https://github.com/lepture/mistune) - Fastest and full featured pure Python parsers of Markdown. - - [Python-Markdown](https://github.com/waylan/Python-Markdown) - A Python implementation of John Gruber’s Markdown. -- YAML - - [PyYAML](http://pyyaml.org/) - YAML implementations for Python. -- CSV - - [csvkit](https://github.com/wireservice/csvkit) - Utilities for converting to and working with CSV. -- Archive - - [unp](https://github.com/mitsuhiko/unp) - A command line tool that can unpack archives easily. +_Functional Programming with Python._ -## Static Site Generator +- [coconut](https://github.com/evhub/coconut) - A variant of Python built for simple, elegant, Pythonic functional programming. +- [functools](https://docs.python.org/3/library/functools.html) - (Python standard library) Higher-order functions and operations on callable objects. +- [funcy](https://github.com/Suor/funcy) - A fancy and practical functional tools. +- [more-itertools](https://github.com/erikrose/more-itertools) - More routines for operating on iterables, beyond `itertools`. +- [returns](https://github.com/dry-python/returns) - A set of type-safe monads, transformers, and composition utilities. +- [toolz](https://github.com/pytoolz/toolz) - A collection of functional utilities for iterators, functions, and dictionaries. Also available as [cytoolz](https://github.com/pytoolz/cytoolz/) for Cython-accelerated performance. -_Static site generator is a software that takes some text + templates as input and produces HTML files on the output._ +## Asynchronous Programming -- [lektor](https://github.com/lektor/lektor) - An easy to use static CMS and blog engine. -- [makesite](https://github.com/sunainapai/makesite) - Simple, lightweight, and magic-free static site/blog generator (< 130 lines). -- [mkdocs](https://github.com/mkdocs/mkdocs/) - Markdown friendly documentation generator. -- [nikola](https://github.com/getnikola/nikola) - A static website and blog generator. -- [pelican](https://github.com/getpelican/pelican) - Static site generator that supports Markdown and reST syntax. +_Libraries for asynchronous, concurrent and parallel execution. Also see [awesome-asyncio](https://github.com/timofurrer/awesome-asyncio)._ -## Task Queues +- [anyio](https://github.com/agronholm/anyio) - A high-level async concurrency and networking framework that works on top of asyncio or trio. +- [asyncio](https://docs.python.org/3/library/asyncio.html) - (Python standard library) Asynchronous I/O, event loop, coroutines and tasks. + - [awesome-asyncio](https://github.com/timofurrer/awesome-asyncio) +- [concurrent.futures](https://docs.python.org/3/library/concurrent.futures.html) - (Python standard library) A high-level interface for asynchronously executing callables. +- [gevent](https://github.com/gevent/gevent) - A coroutine-based Python networking library that uses [greenlet](https://github.com/python-greenlet/greenlet). +- [multiprocessing](https://docs.python.org/3/library/multiprocessing.html) - (Python standard library) Process-based parallelism. +- [trio](https://github.com/python-trio/trio) - A friendly library for async concurrency and I/O. +- [twisted](https://github.com/twisted/twisted) - An event-driven networking engine. +- [uvloop](https://github.com/MagicStack/uvloop) - Ultra fast asyncio event loop. -_Libraries for working with task queues._ +## Date and Time -- [celery](https://docs.celeryproject.org/en/stable/) - An asynchronous task queue/job queue based on distributed message passing. -- [dramatiq](https://github.com/Bogdanp/dramatiq) - A fast and reliable background task processing library for Python 3. -- [huey](https://github.com/coleifer/huey) - Little multi-threaded task queue. -- [mrq](https://github.com/pricingassistant/mrq) - A distributed worker task queue in Python using Redis & gevent. -- [rq](https://github.com/rq/rq) - Simple job queues for Python. +_Libraries for working with dates and times._ -## Template Engine +- [dateparser](https://github.com/scrapinghub/dateparser) - A Python parser for human-readable dates in dozens of languages. +- [dateutil](https://github.com/dateutil/dateutil) - Extensions to the standard Python [datetime](https://docs.python.org/3/library/datetime.html) module. +- [pendulum](https://github.com/python-pendulum/pendulum) - Python datetimes made easy. +- [zoneinfo](https://docs.python.org/3/library/zoneinfo.html) - (Python standard library) IANA time zone support. Brings the [tz database](https://en.wikipedia.org/wiki/Tz_database) into Python. -_Libraries and tools for templating and lexing._ +**Python Toolchain** -- [Genshi](https://genshi.edgewall.org/) - Python templating toolkit for generation of web-aware output. -- [Jinja2](https://github.com/pallets/jinja) - A modern and designer friendly templating language. -- [Mako](http://www.makotemplates.org/) - Hyperfast and lightweight templating for the Python platform. +## Environment Management -## Testing +_Libraries for Python version and virtual environment management._ -_Libraries for testing codebases and generating test data._ +- [pyenv](https://github.com/pyenv/pyenv) - Simple Python version management. + - [pyenv-win](https://github.com/pyenv-win/pyenv-win) - Pyenv for Windows, Simple Python version management. +- [uv](https://github.com/astral-sh/uv) - An extremely fast Python version, package and project manager, written in Rust. +- [virtualenv](https://github.com/pypa/virtualenv) - A tool to create isolated Python environments. -- Testing Frameworks - - [hypothesis](https://github.com/HypothesisWorks/hypothesis) - Hypothesis is an advanced Quickcheck style property based testing library. - - [nose2](https://github.com/nose-devs/nose2) - The successor to `nose`, based on `unittest2`. - - [pytest](https://docs.pytest.org/en/latest/) - A mature full-featured Python testing tool. - - [Robot Framework](https://github.com/robotframework/robotframework) - A generic test automation framework. - - [ScanAPI](https://pypi.org/project/scanapi/) - Automated Testing and Documentation for your REST API. - - [unittest](https://docs.python.org/3/library/unittest.html) - (Python standard library) Unit testing framework. -- Test Runners - - [green](https://github.com/CleanCut/green) - A clean, colorful test runner. - - [mamba](http://nestorsalceda.github.io/mamba/) - The definitive testing tool for Python. Born under the banner of BDD. - - [tox](https://tox.readthedocs.io/en/latest/) - Auto builds and tests distributions in multiple Python versions -- GUI / Web Testing - - [locust](https://github.com/locustio/locust) - Scalable user load testing tool written in Python. - - [PyAutoGUI](https://github.com/asweigart/pyautogui) - PyAutoGUI is a cross-platform GUI automation Python module for human beings. - - [Schemathesis](https://github.com/kiwicom/schemathesis) - A tool for automatic property-based testing of web applications built with Open API / Swagger specifications. - - [Selenium](https://pypi.org/project/selenium/) - Python bindings for [Selenium](https://selenium.dev/) [WebDriver](https://selenium.dev/documentation/webdriver/). - - [splinter](https://github.com/cobrateam/splinter) - Open source tool for testing web applications. -- Mock - - [doublex](https://pypi.org/project/doublex/) - Powerful test doubles framework for Python. - - [freezegun](https://github.com/spulec/freezegun) - Travel through time by mocking the datetime module. - - [httmock](https://github.com/patrys/httmock) - A mocking library for requests for Python 2.6+ and 3.2+. - - [httpretty](https://github.com/gabrielfalcao/HTTPretty) - HTTP request mock tool for Python. - - [mock](https://docs.python.org/3/library/unittest.mock.html) - (Python standard library) A mocking and patching library. - - [mocket](https://github.com/mindflayer/python-mocket) - A socket mock framework with gevent/asyncio/SSL support. - - [responses](https://github.com/getsentry/responses) - A utility library for mocking out the requests Python library. - - [VCR.py](https://github.com/kevin1024/vcrpy) - Record and replay HTTP interactions on your tests. -- Object Factories - - [factory_boy](https://github.com/FactoryBoy/factory_boy) - A test fixtures replacement for Python. - - [mixer](https://github.com/klen/mixer) - Another fixtures replacement. Supports Django, Flask, SQLAlchemy, Peewee and etc. - - [polyfactory](https://github.com/litestar-org/polyfactory) - mock data generation library with support to classes (continuation of `pydantic-factories`) -- Code Coverage - - [coverage](https://pypi.org/project/coverage/) - Code coverage measurement. -- Fake Data - - [faker](https://github.com/joke2k/faker) - A Python package that generates fake data. - - [mimesis](https://github.com/lk-geimfari/mimesis) - is a Python library that help you generate fake data. +## Package Management -## Text Processing +_Libraries for package and dependency management._ -_Libraries for parsing and manipulating plain texts._ +- [conda](https://github.com/conda/conda/) - Cross-platform, Python-agnostic binary package manager. +- [pip](https://github.com/pypa/pip) - The package installer for Python. +- [pipx](https://github.com/pypa/pipx) - Install and Run Python Applications in Isolated Environments. Like `npx` in Node.js. +- [poetry](https://github.com/python-poetry/poetry) - Python dependency management and packaging made easy. +- [uv](https://github.com/astral-sh/uv) - An extremely fast Python version, package and project manager, written in Rust. -- General - - [chardet](https://github.com/chardet/chardet) - Python 2/3 compatible character encoding detector. - - [difflib](https://docs.python.org/3/library/difflib.html) - (Python standard library) Helpers for computing deltas. - - [ftfy](https://github.com/LuminosoInsight/python-ftfy) - Makes Unicode text less broken and more consistent automagically. - - [Levenshtein](https://github.com/ztane/python-Levenshtein/) - Fast computation of Levenshtein distance and string similarity. - - [pangu.py](https://github.com/vinta/pangu.py) - Paranoid text spacing. - - [pyfiglet](https://github.com/pwaller/pyfiglet) - An implementation of figlet written in Python. - - [pypinyin](https://github.com/mozillazg/python-pinyin) - Convert Chinese hanzi (漢字) to pinyin (拼音). - - [textdistance](https://github.com/orsinium/textdistance) - Compute distance between sequences with 30+ algorithms. - - [unidecode](https://pypi.org/project/Unidecode/) - ASCII transliterations of Unicode text. -- Slugify - - [awesome-slugify](https://github.com/dimka665/awesome-slugify) - A Python slugify library that can preserve unicode. - - [python-slugify](https://github.com/un33k/python-slugify) - A Python slugify library that translates unicode to ASCII. - - [unicode-slugify](https://github.com/mozilla/unicode-slugify) - A slugifier that generates unicode slugs with Django as a dependency. -- Unique identifiers - - [hashids](https://github.com/davidaurelio/hashids-python) - Implementation of [hashids](http://hashids.org) in Python. - - [shortuuid](https://github.com/skorokithakis/shortuuid) - A generator library for concise, unambiguous and URL-safe UUIDs. -- Parser - - [pygments](http://pygments.org/) - A generic syntax highlighter. - - [pyparsing](https://github.com/pyparsing/pyparsing) - A general purpose framework for generating parsers. - - [python-nameparser](https://github.com/derek73/python-nameparser) - Parsing human names into their individual components. - - [python-phonenumbers](https://github.com/daviddrysdale/python-phonenumbers) - Parsing, formatting, storing and validating international phone numbers. - - [python-user-agents](https://github.com/selwin/python-user-agents) - Browser user agent parser. - - [sqlparse](https://github.com/andialbrecht/sqlparse) - A non-validating SQL parser. +## Package Repositories -## URL Manipulation +_Local PyPI repository server and proxies._ -_Libraries for parsing URLs._ +- [bandersnatch](https://github.com/pypa/bandersnatch/) - PyPI mirroring tool provided by Python Packaging Authority (PyPA). +- [devpi](https://github.com/devpi/devpi) - PyPI server and packaging/testing/release tool. +- [warehouse](https://github.com/pypa/warehouse) - Next generation Python Package Repository (PyPI). -- [furl](https://github.com/gruns/furl) - A small Python library that makes parsing and manipulating URLs easy. -- [purl](https://github.com/codeinthehole/purl) - A simple, immutable URL class with a clean API for interrogation and manipulation. -- [webargs](https://github.com/marshmallow-code/webargs) - A friendly library for parsing HTTP request arguments with built-in support for popular web frameworks. +## Distribution -## Video +_Libraries to create packaged executables for release distribution._ -_Libraries for manipulating video and GIFs._ +- [cx-Freeze](https://github.com/marcelotduarte/cx_Freeze) - It is a Python tool that converts Python scripts into standalone executables and installers for Windows, macOS, and Linux. +- [Nuitka](https://github.com/Nuitka/Nuitka) - Compiles Python programs into high-performance standalone executables (cross-platform, supports all Python versions). +- [pyarmor](https://github.com/dashingsoft/pyarmor) - A tool used to obfuscate python scripts, bind obfuscated scripts to fixed machine or expire obfuscated scripts. +- [pyinstaller](https://github.com/pyinstaller/pyinstaller) - Converts Python programs into stand-alone executables (cross-platform). +- [shiv](https://github.com/linkedin/shiv) - A command line utility for building fully self-contained zipapps (PEP 441), but with all their dependencies included. -- [moviepy](https://zulko.github.io/moviepy/) - A module for script-based movie editing with many formats, including animated GIFs. -- [scikit-video](https://github.com/aizvorski/scikit-video) - Video processing routines for SciPy. -- [vidgear](https://github.com/abhiTronix/vidgear) - Most Powerful multi-threaded Video Processing framework. +## Configuration Files -## Web Asset Management +_Libraries for storing and parsing configuration options._ -_Tools for managing, compressing and minifying website assets._ +- [configparser](https://docs.python.org/3/library/configparser.html) - (Python standard library) INI file parser. +- [dynaconf](https://github.com/dynaconf/dynaconf) - Dynaconf is a configuration manager with plugins for Django, Flask and FastAPI. +- [hydra](https://github.com/facebookresearch/hydra) - Hydra is a framework for elegantly configuring complex applications. +- [python-decouple](https://github.com/HBNetwork/python-decouple) - Strict separation of settings from code. +- [python-dotenv](https://github.com/theskumar/python-dotenv) - Reads key-value pairs from a `.env` file and sets them as environment variables. -- [django-compressor](https://github.com/django-compressor/django-compressor) - Compresses linked and inline JavaScript or CSS into a single cached file. -- [django-pipeline](https://github.com/jazzband/django-pipeline) - An asset packaging library for Django. -- [django-storages](https://github.com/jschneier/django-storages) - A collection of custom storage back ends for Django. -- [fanstatic](http://www.fanstatic.org/en/latest/) - Packages, optimizes, and serves static file dependencies as Python packages. -- [flask-assets](https://github.com/miracle2k/flask-assets) - Helps you integrate webassets into your Flask app. -- [webassets](https://github.com/miracle2k/webassets) - Bundles, optimizes, and manages unique cache-busting URLs for static resources. +**Security** -## Web Content Extracting +## Cryptography -_Libraries for extracting web contents._ +- [cryptography](https://github.com/pyca/cryptography) - A package designed to expose cryptographic primitives and recipes to Python developers. +- [paramiko](https://github.com/paramiko/paramiko) - The leading native Python SSHv2 protocol library. +- [pynacl](https://github.com/pyca/pynacl) - Python binding to the Networking and Cryptography (NaCl) library. -- [html2text](https://github.com/Alir3z4/html2text) - Convert HTML to Markdown-formatted text. -- [lassie](https://github.com/michaelhelmick/lassie) - Web Content Retrieval for Humans. -- [micawber](https://github.com/coleifer/micawber) - A small library for extracting rich content from URLs. -- [newspaper](https://github.com/codelucas/newspaper) - News extraction, article extraction and content curation in Python. -- [python-readability](https://github.com/buriy/python-readability) - Fast Python port of arc90's readability tool. -- [requests-html](https://github.com/psf/requests-html) - Pythonic HTML Parsing for Humans. -- [sumy](https://github.com/miso-belica/sumy) - A module for automatic summarization of text documents and HTML pages. -- [textract](https://github.com/deanmalmgren/textract) - Extract text from any document, Word, PowerPoint, PDFs, etc. -- [toapi](https://github.com/gaojiuli/toapi) - Every web site provides APIs. +## Penetration Testing -## Web Crawling +_Frameworks and tools for penetration testing._ -_Libraries to automate web scraping._ +- [mitmproxy](https://github.com/mitmproxy/mitmproxy) - An interactive TLS-capable intercepting HTTP proxy for penetration testers and software developers. +- [setoolkit](https://github.com/trustedsec/social-engineer-toolkit) - A toolkit for social engineering. +- [sherlock](https://github.com/sherlock-project/sherlock) - Hunt down social media accounts by username across social networks. +- [sqlmap](https://github.com/sqlmapproject/sqlmap) - Automatic SQL injection and database takeover tool. -- [browser-use](https://github.com/browser-use/browser-use) - Make websites accessible for AI agents with easy browser automation. -- [crawl4ai](https://github.com/unclecode/crawl4ai) - An open-source, LLM-friendly web crawler that provides lightning-fast, structured data extraction specifically designed for AI agents. -- [feedparser](https://github.com/kurtmckee/feedparser) - Universal feed parser. -- [grab](https://github.com/lorien/grab) - Site scraping framework. -- [mechanicalsoup](https://github.com/MechanicalSoup/MechanicalSoup) - A Python library for automating interaction with websites. -- [scrapy](https://github.com/scrapy/scrapy) - A fast high-level screen scraping and web crawling framework. +**Miscellaneous** -## Web Frameworks +## Hardware -_Traditional full stack web frameworks. Also see [RESTful API](https://github.com/vinta/awesome-python#restful-api)._ +_Libraries for programming with hardware._ -- Synchronous - - [django](https://github.com/django/django) - The most popular web framework in Python. - - [awesome-django](https://github.com/shahraizali/awesome-django) - - [flask](https://github.com/pallets/flask) - A microframework for Python. - - [awesome-flask](https://github.com/humiaozuzu/awesome-flask) - - [pyramid](https://pylonsproject.org/) - A small, fast, down-to-earth, open source Python web framework. - - [awesome-pyramid](https://github.com/uralbash/awesome-pyramid) - - [fastHTML](https://github.com/AnswerDotAI/fasthtml) - The fastest way to create an HTML app. - - [awesome-fasthtml](https://github.com/amosgyamfi/awesome-fasthtml) - - [masonite](https://github.com/MasoniteFramework/masonite) - The modern and developer centric Python web framework. -- Asynchronous - - [Litestar](https://github.com/litestar-org/litestar) - Production-ready, capable and extensible ASGI Web framework. - - [microdot](https://github.com/miguelgrinberg/microdot) - The impossibly small web framework for Python and MicroPython. - - [reflex](https://github.com/reflex-dev/reflex) – A framework for building reactive, full-stack web applications entirely with python . - - [tornado](https://github.com/tornadoweb/tornado) - A web framework and asynchronous networking library. +- [bleak](https://github.com/hbldh/bleak) - A cross platform Bluetooth Low Energy Client for Python using asyncio. +- [mouse](https://github.com/boppreh/mouse) - Hook and simulate global mouse events on Windows and Linux. +- [pynput](https://github.com/moses-palmer/pynput) - A library to control and monitor input devices. -## WebSocket +## Microsoft Windows -_Libraries for working with WebSocket._ +_Python programming on Microsoft Windows._ -- [autobahn-python](https://github.com/crossbario/autobahn-python) - WebSocket & WAMP for Python on Twisted and [asyncio](https://docs.python.org/3/library/asyncio.html). -- [channels](https://github.com/django/channels) - Developer-friendly asynchrony for Django. -- [websockets](https://github.com/aaugustin/websockets) - A library for building WebSocket servers and clients with a focus on correctness and simplicity. +- [pythonnet](https://github.com/pythonnet/pythonnet) - Python Integration with the .NET Common Language Runtime (CLR). +- [pywin32](https://github.com/mhammond/pywin32) - Python Extensions for Windows. +- [winpython](https://github.com/winpython/winpython) - Portable development environment for Windows 10/11. -## WSGI Servers +## Miscellaneous -_WSGI-compatible web servers._ +_Useful libraries or tools that don't fit in the categories above._ -- [gunicorn](https://github.com/benoitc/gunicorn) - Pre-forked, ported from Ruby's Unicorn project. -- [uwsgi](https://uwsgi-docs.readthedocs.io/en/latest/) - A project aims at developing a full stack for building hosting services, written in C. -- [waitress](https://github.com/Pylons/waitress) - Multi-threaded, powers Pyramid. -- [werkzeug](https://github.com/pallets/werkzeug) - A WSGI utility library for Python that powers Flask and can easily be embedded into your own projects. +- [blinker](https://github.com/jek/blinker) - A fast Python in-process signal/event dispatching system. +- [boltons](https://github.com/mahmoud/boltons) - A set of pure-Python utilities. +- [itsdangerous](https://github.com/pallets/itsdangerous) - Various helpers to pass trusted data to untrusted environments. +- [tryton](https://github.com/tryton/tryton) - A general-purpose business framework. # Resources diff --git a/docs/CNAME b/docs/CNAME deleted file mode 100644 index 0f6ced6634..0000000000 --- a/docs/CNAME +++ /dev/null @@ -1 +0,0 @@ -awesome-python.com \ No newline at end of file diff --git a/docs/css/extra.css b/docs/css/extra.css deleted file mode 100644 index 1230d796a6..0000000000 --- a/docs/css/extra.css +++ /dev/null @@ -1,9 +0,0 @@ -@media (min-width: 960px) { - html { - scroll-behavior: smooth; - } - - .md-content__inner > ul:nth-child(5) { - display: none; - } -} diff --git a/mkdocs.yml b/mkdocs.yml deleted file mode 100644 index 67a0373a4b..0000000000 --- a/mkdocs.yml +++ /dev/null @@ -1,26 +0,0 @@ -site_name: Awesome Python -site_url: https://awesome-python.com -site_description: A curated list of awesome Python frameworks, libraries and software -site_author: Vinta Chen -repo_name: vinta/awesome-python -repo_url: https://github.com/vinta/awesome-python -theme: - name: material - palette: - primary: red - accent: pink -extra: - social: - - type: github - link: https://github.com/vinta - - type: twitter - link: https://twitter.com/vinta - - type: linkedin - link: https://www.linkedin.com/in/vinta -google_analytics: - - UA-510626-7 - - auto -extra_css: - - css/extra.css -nav: - - "Life is short, you need Python.": "index.md" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000000..31fee98f1b --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,31 @@ +[project] +name = "awesome-python" +version = "0.1.0" +description = "An opinionated list of awesome Python frameworks, libraries, software and resources." +authors = [{ name = "Vinta Chen", email = "vinta.chen@gmail.com" }] +readme = "README.md" +license = "MIT" +requires-python = ">=3.13" +dependencies = [] + +[project.urls] +Homepage = "https://awesome-python.com/" +Repository = "https://github.com/vinta/awesome-python" + +[dependency-groups] +build = ["httpx==0.28.1", "jinja2==3.1.6", "markdown-it-py==4.0.0"] +lint = ["ruff==0.15.6"] +test = ["pytest==9.0.2"] +dev = [ + { include-group = "build" }, + { include-group = "lint" }, + { include-group = "test" }, + "watchdog==6.0.0", +] + +[tool.pytest.ini_options] +testpaths = ["website/tests"] +pythonpath = ["website"] + +[tool.ruff] +line-length = 200 diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 89d64c3034..0000000000 --- a/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -mkdocs==1.0.4 -mkdocs-material==4.0.2 diff --git a/sort.py b/sort.py deleted file mode 100755 index 431bd02b48..0000000000 --- a/sort.py +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/env python -# coding: utf-8 - -""" - The approach taken is explained below. I decided to do it simply. - Initially I was considering parsing the data into some sort of - structure and then generating an appropriate README. I am still - considering doing it - but for now this should work. The only issue - I see is that it only sorts the entries at the lowest level, and that - the order of the top-level contents do not match the order of the actual - entries. - - This could be extended by having nested blocks, sorting them recursively - and flattening the end structure into a list of lines. Revision 2 maybe ^.^. -""" - -def sort_blocks(): - # First, we load the current README into memory - with open('README.md', 'r') as read_me_file: - read_me = read_me_file.read() - - # Separating the 'table of contents' from the contents (blocks) - table_of_contents = ''.join(read_me.split('- - -')[0]) - blocks = ''.join(read_me.split('- - -')[1]).split('\n# ') - for i in range(len(blocks)): - if i == 0: - blocks[i] = blocks[i] + '\n' - else: - blocks[i] = '# ' + blocks[i] + '\n' - - # Sorting the libraries - inner_blocks = sorted(blocks[0].split('##')) - for i in range(1, len(inner_blocks)): - if inner_blocks[i][0] != '#': - inner_blocks[i] = '##' + inner_blocks[i] - inner_blocks = ''.join(inner_blocks) - - # Replacing the non-sorted libraries by the sorted ones and gathering all at the final_README file - blocks[0] = inner_blocks - final_README = table_of_contents + '- - -' + ''.join(blocks) - - with open('README.md', 'w+') as sorted_file: - sorted_file.write(final_README) - -def main(): - # First, we load the current README into memory as an array of lines - with open('README.md', 'r') as read_me_file: - read_me = read_me_file.readlines() - - # Then we cluster the lines together as blocks - # Each block represents a collection of lines that should be sorted - # This was done by assuming only links ([...](...)) are meant to be sorted - # Clustering is done by indentation - blocks = [] - last_indent = None - for line in read_me: - s_line = line.lstrip() - indent = len(line) - len(s_line) - - if any([s_line.startswith(s) for s in ['* [', '- [']]): - if indent == last_indent: - blocks[-1].append(line) - else: - blocks.append([line]) - last_indent = indent - else: - blocks.append([line]) - last_indent = None - - with open('README.md', 'w+') as sorted_file: - # Then all of the blocks are sorted individually - blocks = [ - ''.join(sorted(block, key=str.lower)) for block in blocks - ] - # And the result is written back to README.md - sorted_file.write(''.join(blocks)) - - # Then we call the sorting method - sort_blocks() - - -if __name__ == "__main__": - main() diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000000..ecff72915b --- /dev/null +++ b/uv.lock @@ -0,0 +1,307 @@ +version = 1 +revision = 3 +requires-python = ">=3.13" + +[[package]] +name = "anyio" +version = "4.12.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" }, +] + +[[package]] +name = "awesome-python" +version = "0.1.0" +source = { virtual = "." } + +[package.dev-dependencies] +build = [ + { name = "httpx" }, + { name = "jinja2" }, + { name = "markdown-it-py" }, +] +dev = [ + { name = "httpx" }, + { name = "jinja2" }, + { name = "markdown-it-py" }, + { name = "pytest" }, + { name = "ruff" }, + { name = "watchdog" }, +] +lint = [ + { name = "ruff" }, +] +test = [ + { name = "pytest" }, +] + +[package.metadata] + +[package.metadata.requires-dev] +build = [ + { name = "httpx", specifier = "==0.28.1" }, + { name = "jinja2", specifier = "==3.1.6" }, + { name = "markdown-it-py", specifier = "==4.0.0" }, +] +dev = [ + { name = "httpx", specifier = "==0.28.1" }, + { name = "jinja2", specifier = "==3.1.6" }, + { name = "markdown-it-py", specifier = "==4.0.0" }, + { name = "pytest", specifier = "==9.0.2" }, + { name = "ruff", specifier = "==0.15.6" }, + { name = "watchdog", specifier = "==6.0.0" }, +] +lint = [{ name = "ruff", specifier = "==0.15.6" }] +test = [{ name = "pytest", specifier = "==9.0.2" }] + +[[package]] +name = "certifi" +version = "2026.2.25" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + +[[package]] +name = "idna" +version = "3.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + +[[package]] +name = "markdown-it-py" +version = "4.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, + { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, + { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, + { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, + { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, + { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, + { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, + { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, + { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, + { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, + { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, + { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, + { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, + { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, + { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, + { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, + { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, + { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, + { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, + { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, + { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, + { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, + { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, + { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + +[[package]] +name = "packaging" +version = "26.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "pygments" +version = "2.19.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, +] + +[[package]] +name = "pytest" +version = "9.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, +] + +[[package]] +name = "ruff" +version = "0.15.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/51/df/f8629c19c5318601d3121e230f74cbee7a3732339c52b21daa2b82ef9c7d/ruff-0.15.6.tar.gz", hash = "sha256:8394c7bb153a4e3811a4ecdacd4a8e6a4fa8097028119160dffecdcdf9b56ae4", size = 4597916, upload-time = "2026-03-12T23:05:47.51Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/2f/4e03a7e5ce99b517e98d3b4951f411de2b0fa8348d39cf446671adcce9a2/ruff-0.15.6-py3-none-linux_armv6l.whl", hash = "sha256:7c98c3b16407b2cf3d0f2b80c80187384bc92c6774d85fefa913ecd941256fff", size = 10508953, upload-time = "2026-03-12T23:05:17.246Z" }, + { url = "https://files.pythonhosted.org/packages/70/60/55bcdc3e9f80bcf39edf0cd272da6fa511a3d94d5a0dd9e0adf76ceebdb4/ruff-0.15.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ee7dcfaad8b282a284df4aa6ddc2741b3f4a18b0555d626805555a820ea181c3", size = 10942257, upload-time = "2026-03-12T23:05:23.076Z" }, + { url = "https://files.pythonhosted.org/packages/e7/f9/005c29bd1726c0f492bfa215e95154cf480574140cb5f867c797c18c790b/ruff-0.15.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3bd9967851a25f038fc8b9ae88a7fbd1b609f30349231dffaa37b6804923c4bb", size = 10322683, upload-time = "2026-03-12T23:05:33.738Z" }, + { url = "https://files.pythonhosted.org/packages/5f/74/2f861f5fd7cbb2146bddb5501450300ce41562da36d21868c69b7a828169/ruff-0.15.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13f4594b04e42cd24a41da653886b04d2ff87adbf57497ed4f728b0e8a4866f8", size = 10660986, upload-time = "2026-03-12T23:05:53.245Z" }, + { url = "https://files.pythonhosted.org/packages/c1/a1/309f2364a424eccb763cdafc49df843c282609f47fe53aa83f38272389e0/ruff-0.15.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e2ed8aea2f3fe57886d3f00ea5b8aae5bf68d5e195f487f037a955ff9fbaac9e", size = 10332177, upload-time = "2026-03-12T23:05:56.145Z" }, + { url = "https://files.pythonhosted.org/packages/30/41/7ebf1d32658b4bab20f8ac80972fb19cd4e2c6b78552be263a680edc55ac/ruff-0.15.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70789d3e7830b848b548aae96766431c0dc01a6c78c13381f423bf7076c66d15", size = 11170783, upload-time = "2026-03-12T23:06:01.742Z" }, + { url = "https://files.pythonhosted.org/packages/76/be/6d488f6adca047df82cd62c304638bcb00821c36bd4881cfca221561fdfc/ruff-0.15.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:542aaf1de3154cea088ced5a819ce872611256ffe2498e750bbae5247a8114e9", size = 12044201, upload-time = "2026-03-12T23:05:28.697Z" }, + { url = "https://files.pythonhosted.org/packages/71/68/e6f125df4af7e6d0b498f8d373274794bc5156b324e8ab4bf5c1b4fc0ec7/ruff-0.15.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c22e6f02c16cfac3888aa636e9eba857254d15bbacc9906c9689fdecb1953ab", size = 11421561, upload-time = "2026-03-12T23:05:31.236Z" }, + { url = "https://files.pythonhosted.org/packages/f1/9f/f85ef5fd01a52e0b472b26dc1b4bd228b8f6f0435975442ffa4741278703/ruff-0.15.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98893c4c0aadc8e448cfa315bd0cc343a5323d740fe5f28ef8a3f9e21b381f7e", size = 11310928, upload-time = "2026-03-12T23:05:45.288Z" }, + { url = "https://files.pythonhosted.org/packages/8c/26/b75f8c421f5654304b89471ed384ae8c7f42b4dff58fa6ce1626d7f2b59a/ruff-0.15.6-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:70d263770d234912374493e8cc1e7385c5d49376e41dfa51c5c3453169dc581c", size = 11235186, upload-time = "2026-03-12T23:05:50.677Z" }, + { url = "https://files.pythonhosted.org/packages/fc/d4/d5a6d065962ff7a68a86c9b4f5500f7d101a0792078de636526c0edd40da/ruff-0.15.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:55a1ad63c5a6e54b1f21b7514dfadc0c7fb40093fa22e95143cf3f64ebdcd512", size = 10635231, upload-time = "2026-03-12T23:05:37.044Z" }, + { url = "https://files.pythonhosted.org/packages/d6/56/7c3acf3d50910375349016cf33de24be021532042afbed87942858992491/ruff-0.15.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8dc473ba093c5ec238bb1e7429ee676dca24643c471e11fbaa8a857925b061c0", size = 10340357, upload-time = "2026-03-12T23:06:04.748Z" }, + { url = "https://files.pythonhosted.org/packages/06/54/6faa39e9c1033ff6a3b6e76b5df536931cd30caf64988e112bbf91ef5ce5/ruff-0.15.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:85b042377c2a5561131767974617006f99f7e13c63c111b998f29fc1e58a4cfb", size = 10860583, upload-time = "2026-03-12T23:05:58.978Z" }, + { url = "https://files.pythonhosted.org/packages/cb/1e/509a201b843b4dfb0b32acdedf68d951d3377988cae43949ba4c4133a96a/ruff-0.15.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:cef49e30bc5a86a6a92098a7fbf6e467a234d90b63305d6f3ec01225a9d092e0", size = 11410976, upload-time = "2026-03-12T23:05:39.955Z" }, + { url = "https://files.pythonhosted.org/packages/6c/25/3fc9114abf979a41673ce877c08016f8e660ad6cf508c3957f537d2e9fa9/ruff-0.15.6-py3-none-win32.whl", hash = "sha256:bbf67d39832404812a2d23020dda68fee7f18ce15654e96fb1d3ad21a5fe436c", size = 10616872, upload-time = "2026-03-12T23:05:42.451Z" }, + { url = "https://files.pythonhosted.org/packages/89/7a/09ece68445ceac348df06e08bf75db72d0e8427765b96c9c0ffabc1be1d9/ruff-0.15.6-py3-none-win_amd64.whl", hash = "sha256:aee25bc84c2f1007ecb5037dff75cef00414fdf17c23f07dc13e577883dca406", size = 11787271, upload-time = "2026-03-12T23:05:20.168Z" }, + { url = "https://files.pythonhosted.org/packages/7f/d0/578c47dd68152ddddddf31cd7fc67dc30b7cdf639a86275fda821b0d9d98/ruff-0.15.6-py3-none-win_arm64.whl", hash = "sha256:c34de3dd0b0ba203be50ae70f5910b17188556630e2178fd7d79fc030eb0d837", size = 11060497, upload-time = "2026-03-12T23:05:25.968Z" }, +] + +[[package]] +name = "watchdog" +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480, upload-time = "2024-11-01T14:06:42.952Z" }, + { url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451, upload-time = "2024-11-01T14:06:45.084Z" }, + { url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057, upload-time = "2024-11-01T14:06:47.324Z" }, + { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" }, + { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" }, + { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" }, + { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" }, + { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" }, + { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" }, + { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" }, + { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" }, + { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" }, +] diff --git a/website/build.py b/website/build.py new file mode 100644 index 0000000000..1645e83b98 --- /dev/null +++ b/website/build.py @@ -0,0 +1,194 @@ +#!/usr/bin/env python3 +"""Build a single-page HTML site from README.md for the awesome-python website.""" + +import json +import re +import shutil +from pathlib import Path +from typing import TypedDict + +from jinja2 import Environment, FileSystemLoader +from readme_parser import parse_readme, slugify + + +def group_categories( + parsed_groups: list[dict], + resources: list[dict], +) -> list[dict]: + """Combine parsed groups with resources for template rendering.""" + groups = list(parsed_groups) + + if resources: + groups.append( + { + "name": "Resources", + "slug": slugify("Resources"), + "categories": list(resources), + } + ) + + return groups + + +class Entry(TypedDict): + name: str + url: str + description: str + category: str + group: str + stars: int | None + owner: str | None + last_commit_at: str | None + + +class StarData(TypedDict): + stars: int + owner: str + last_commit_at: str + fetched_at: str + + +GITHUB_REPO_URL_RE = re.compile(r"^https?://github\.com/([^/]+/[^/]+?)(?:\.git)?/?$") + + +def extract_github_repo(url: str) -> str | None: + """Extract owner/repo from a GitHub repo URL. Returns None for non-GitHub URLs.""" + m = GITHUB_REPO_URL_RE.match(url) + return m.group(1) if m else None + + +def load_stars(path: Path) -> dict[str, StarData]: + """Load star data from JSON. Returns empty dict if file doesn't exist or is corrupt.""" + if path.exists(): + try: + return json.loads(path.read_text(encoding="utf-8")) + except json.JSONDecodeError: + return {} + return {} + + +def sort_entries(entries: list[dict]) -> list[dict]: + """Sort entries by stars descending, then name ascending. No-star entries go last.""" + + def sort_key(entry: dict) -> tuple[int, int, str]: + stars = entry["stars"] + name = entry["name"].lower() + if stars is None: + return (1, 0, name) + return (0, -stars, name) + + return sorted(entries, key=sort_key) + + +def extract_entries( + categories: list[dict], + groups: list[dict], +) -> list[dict]: + """Flatten categories into individual library entries for table display. + + Entries appearing in multiple categories are merged into a single entry + with lists of categories and groups. + """ + cat_to_group: dict[str, str] = {} + for group in groups: + for cat in group["categories"]: + cat_to_group[cat["name"]] = group["name"] + + seen: dict[str, dict] = {} # url -> entry + entries: list[dict] = [] + for cat in categories: + group_name = cat_to_group.get(cat["name"], "Other") + for entry in cat["entries"]: + url = entry["url"] + if url in seen: + existing = seen[url] + if cat["name"] not in existing["categories"]: + existing["categories"].append(cat["name"]) + if group_name not in existing["groups"]: + existing["groups"].append(group_name) + else: + merged = { + "name": entry["name"], + "url": url, + "description": entry["description"], + "categories": [cat["name"]], + "groups": [group_name], + "stars": None, + "owner": None, + "last_commit_at": None, + "also_see": entry["also_see"], + } + seen[url] = merged + entries.append(merged) + return entries + + +def build(repo_root: str) -> None: + """Main build: parse README, render single-page HTML via Jinja2 templates.""" + repo = Path(repo_root) + website = repo / "website" + readme_text = (repo / "README.md").read_text(encoding="utf-8") + + subtitle = "" + for line in readme_text.split("\n"): + stripped = line.strip() + if stripped and not stripped.startswith("#"): + subtitle = stripped + break + + parsed_groups, resources = parse_readme(readme_text) + + categories = [cat for g in parsed_groups for cat in g["categories"]] + total_entries = sum(c["entry_count"] for c in categories) + groups = group_categories(parsed_groups, resources) + entries = extract_entries(categories, groups) + + stars_data = load_stars(website / "data" / "github_stars.json") + for entry in entries: + repo_key = extract_github_repo(entry["url"]) + if repo_key and repo_key in stars_data: + sd = stars_data[repo_key] + entry["stars"] = sd["stars"] + entry["owner"] = sd["owner"] + entry["last_commit_at"] = sd.get("last_commit_at", "") + + entries = sort_entries(entries) + + env = Environment( + loader=FileSystemLoader(website / "templates"), + autoescape=True, + ) + + site_dir = website / "output" + if site_dir.exists(): + shutil.rmtree(site_dir) + site_dir.mkdir(parents=True) + + tpl_index = env.get_template("index.html") + (site_dir / "index.html").write_text( + tpl_index.render( + categories=categories, + resources=resources, + groups=groups, + subtitle=subtitle, + entries=entries, + total_entries=total_entries, + total_categories=len(categories), + ), + encoding="utf-8", + ) + + static_src = website / "static" + static_dst = site_dir / "static" + if static_src.exists(): + shutil.copytree(static_src, static_dst, dirs_exist_ok=True) + + shutil.copy(repo / "README.md", site_dir / "llms.txt") + + print(f"Built single page with {len(parsed_groups)} groups, {len(categories)} categories + {len(resources)} resources") + print(f"Total entries: {total_entries}") + print(f"Output: {site_dir}") + + +if __name__ == "__main__": + build(str(Path(__file__).parent.parent)) diff --git a/website/fetch_github_stars.py b/website/fetch_github_stars.py new file mode 100644 index 0000000000..c639853148 --- /dev/null +++ b/website/fetch_github_stars.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python3 +"""Fetch GitHub star counts and owner info for all GitHub repos in README.md.""" + +import json +import os +import re +import sys +from datetime import datetime, timezone +from pathlib import Path + +import httpx + +from build import extract_github_repo, load_stars + +CACHE_MAX_AGE_HOURS = 12 +DATA_DIR = Path(__file__).parent / "data" +CACHE_FILE = DATA_DIR / "github_stars.json" +README_PATH = Path(__file__).parent.parent / "README.md" +GRAPHQL_URL = "https://api.github.com/graphql" +BATCH_SIZE = 50 + + +def extract_github_repos(text: str) -> set[str]: + """Extract unique owner/repo pairs from GitHub URLs in markdown text.""" + repos = set() + for url in re.findall(r"https?://github\.com/[^\s)\]]+", text): + repo = extract_github_repo(url.split("#")[0].rstrip("/")) + if repo: + repos.add(repo) + return repos + + +def save_cache(cache: dict) -> None: + """Write the star cache to disk, creating data/ dir if needed.""" + DATA_DIR.mkdir(parents=True, exist_ok=True) + CACHE_FILE.write_text( + json.dumps(cache, indent=2, ensure_ascii=False) + "\n", + encoding="utf-8", + ) + + +def build_graphql_query(repos: list[str]) -> str: + """Build a GraphQL query with aliases for up to 100 repos.""" + if not repos: + return "" + parts = [] + for i, repo in enumerate(repos): + owner, name = repo.split("/", 1) + if '"' in owner or '"' in name: + continue + parts.append( + f'repo_{i}: repository(owner: "{owner}", name: "{name}") ' + f"{{ stargazerCount owner {{ login }} defaultBranchRef {{ target {{ ... on Commit {{ committedDate }} }} }} }}" + ) + if not parts: + return "" + return "query { " + " ".join(parts) + " }" + + +def parse_graphql_response( + data: dict, + repos: list[str], +) -> dict[str, dict]: + """Parse GraphQL response into {owner/repo: {stars, owner}} dict.""" + result = {} + for i, repo in enumerate(repos): + node = data.get(f"repo_{i}") + if node is None: + continue + default_branch = node.get("defaultBranchRef") or {} + target = default_branch.get("target") or {} + result[repo] = { + "stars": node.get("stargazerCount", 0), + "owner": node.get("owner", {}).get("login", ""), + "last_commit_at": target.get("committedDate", ""), + } + return result + + +def fetch_batch( + repos: list[str], *, client: httpx.Client, +) -> dict[str, dict]: + """Fetch star data for a batch of repos via GitHub GraphQL API.""" + query = build_graphql_query(repos) + if not query: + return {} + resp = client.post(GRAPHQL_URL, json={"query": query}) + resp.raise_for_status() + result = resp.json() + if "errors" in result: + for err in result["errors"]: + print(f" Warning: {err.get('message', err)}", file=sys.stderr) + data = result.get("data", {}) + return parse_graphql_response(data, repos) + + +def main() -> None: + """Fetch GitHub stars for all repos in README.md, updating the JSON cache.""" + token = os.environ.get("GITHUB_TOKEN", "") + if not token: + print("Error: GITHUB_TOKEN environment variable is required.", file=sys.stderr) + sys.exit(1) + + readme_text = README_PATH.read_text(encoding="utf-8") + current_repos = extract_github_repos(readme_text) + print(f"Found {len(current_repos)} GitHub repos in README.md") + + cache = load_stars(CACHE_FILE) + now = datetime.now(timezone.utc) + + # Prune entries not in current README + pruned = {k: v for k, v in cache.items() if k in current_repos} + if len(pruned) < len(cache): + print(f"Pruned {len(cache) - len(pruned)} stale cache entries") + cache = pruned + + # Determine which repos need fetching (missing or stale) + to_fetch = [] + for repo in sorted(current_repos): + entry = cache.get(repo) + if entry and "fetched_at" in entry: + fetched = datetime.fromisoformat(entry["fetched_at"]) + age_hours = (now - fetched).total_seconds() / 3600 + if age_hours < CACHE_MAX_AGE_HOURS: + continue + to_fetch.append(repo) + + print(f"{len(to_fetch)} repos to fetch ({len(current_repos) - len(to_fetch)} cached)") + + if not to_fetch: + save_cache(cache) + print("Cache is up to date.") + return + + # Fetch in batches + fetched_count = 0 + skipped_repos: list[str] = [] + + with httpx.Client( + headers={"Authorization": f"bearer {token}", "Content-Type": "application/json"}, + transport=httpx.HTTPTransport(retries=2), + timeout=30, + ) as client: + for i in range(0, len(to_fetch), BATCH_SIZE): + batch = to_fetch[i : i + BATCH_SIZE] + batch_num = i // BATCH_SIZE + 1 + total_batches = (len(to_fetch) + BATCH_SIZE - 1) // BATCH_SIZE + print(f"Fetching batch {batch_num}/{total_batches} ({len(batch)} repos)...") + + try: + results = fetch_batch(batch, client=client) + except httpx.HTTPStatusError as e: + print(f"HTTP error {e.response.status_code}", file=sys.stderr) + if e.response.status_code == 401: + print("Error: Invalid GITHUB_TOKEN.", file=sys.stderr) + sys.exit(1) + print("Saving partial cache and exiting.", file=sys.stderr) + save_cache(cache) + sys.exit(1) + + now_iso = now.isoformat() + for repo in batch: + if repo in results: + cache[repo] = { + "stars": results[repo]["stars"], + "owner": results[repo]["owner"], + "last_commit_at": results[repo]["last_commit_at"], + "fetched_at": now_iso, + } + fetched_count += 1 + else: + skipped_repos.append(repo) + + # Save after each batch in case of interruption + save_cache(cache) + + if skipped_repos: + print(f"Skipped {len(skipped_repos)} repos (deleted/private/renamed)") + print(f"Done. Fetched {fetched_count} repos, {len(cache)} total cached.") + + +if __name__ == "__main__": + main() diff --git a/website/readme_parser.py b/website/readme_parser.py new file mode 100644 index 0000000000..c0ecfc60c5 --- /dev/null +++ b/website/readme_parser.py @@ -0,0 +1,474 @@ +"""Parse README.md into structured section data using markdown-it-py AST.""" + +from __future__ import annotations + +import re +from typing import TypedDict + +from markdown_it import MarkdownIt +from markdown_it.tree import SyntaxTreeNode +from markupsafe import escape + + +class AlsoSee(TypedDict): + name: str + url: str + + +class ParsedEntry(TypedDict): + name: str + url: str + description: str # inline HTML, properly escaped + also_see: list[AlsoSee] + + +class ParsedSection(TypedDict): + name: str + slug: str + description: str # plain text, links resolved to text + entries: list[ParsedEntry] + entry_count: int + preview: str + content_html: str # rendered HTML, properly escaped + + +class ParsedGroup(TypedDict): + name: str + slug: str + categories: list[ParsedSection] + + +# --- Slugify ---------------------------------------------------------------- + +_SLUG_NON_ALNUM_RE = re.compile(r"[^a-z0-9\s-]") +_SLUG_WHITESPACE_RE = re.compile(r"[\s]+") +_SLUG_MULTI_DASH_RE = re.compile(r"-+") + + +def slugify(name: str) -> str: + """Convert a category name to a URL-friendly slug.""" + slug = name.lower() + slug = _SLUG_NON_ALNUM_RE.sub("", slug) + slug = _SLUG_WHITESPACE_RE.sub("-", slug.strip()) + slug = _SLUG_MULTI_DASH_RE.sub("-", slug) + return slug + + +# --- Inline renderers ------------------------------------------------------- + + +def render_inline_html(children: list[SyntaxTreeNode]) -> str: + """Render inline AST nodes to HTML with proper escaping.""" + parts: list[str] = [] + for child in children: + match child.type: + case "text": + parts.append(str(escape(child.content))) + case "softbreak": + parts.append(" ") + case "link": + href = str(escape(child.attrGet("href") or "")) + inner = render_inline_html(child.children) + parts.append( + f'{inner}' + ) + case "em": + parts.append(f"{render_inline_html(child.children)}") + case "strong": + parts.append(f"{render_inline_html(child.children)}") + case "code_inline": + parts.append(f"{escape(child.content)}") + case "html_inline": + parts.append(str(escape(child.content))) + return "".join(parts) + + +def render_inline_text(children: list[SyntaxTreeNode]) -> str: + """Render inline AST nodes to plain text (links become their text).""" + parts: list[str] = [] + for child in children: + match child.type: + case "text": + parts.append(child.content) + case "softbreak": + parts.append(" ") + case "code_inline": + parts.append(child.content) + case "em" | "strong" | "link": + parts.append(render_inline_text(child.children)) + return "".join(parts) + + +# --- AST helpers ------------------------------------------------------------- + + +def _heading_text(node: SyntaxTreeNode) -> str: + """Extract plain text from a heading node.""" + for child in node.children: + if child.type == "inline": + return render_inline_text(child.children) + return "" + + +def _extract_description(nodes: list[SyntaxTreeNode]) -> str: + """Extract description from the first paragraph if it's a single block. + + Pattern: _Libraries for foo._ -> "Libraries for foo." + """ + if not nodes: + return "" + first = nodes[0] + if first.type != "paragraph": + return "" + for child in first.children: + if child.type == "inline" and len(child.children) == 1: + em = child.children[0] + if em.type == "em": + return render_inline_text(em.children) + return "" + + +# --- Entry extraction -------------------------------------------------------- + +_DESC_SEP_RE = re.compile(r"^\s*[-\u2013\u2014]\s*") + + +def _find_child(node: SyntaxTreeNode, child_type: str) -> SyntaxTreeNode | None: + """Find first direct child of a given type.""" + for child in node.children: + if child.type == child_type: + return child + return None + + +def _find_inline(node: SyntaxTreeNode) -> SyntaxTreeNode | None: + """Find the inline node in a list_item's paragraph.""" + para = _find_child(node, "paragraph") + if para is None: + return None + return _find_child(para, "inline") + + +def _find_first_link(inline: SyntaxTreeNode) -> SyntaxTreeNode | None: + """Find the first link node among inline children.""" + for child in inline.children: + if child.type == "link": + return child + return None + + +def _is_leading_link(inline: SyntaxTreeNode, link: SyntaxTreeNode) -> bool: + """Check if the link is the first child of inline (a real entry, not a subcategory label).""" + return bool(inline.children) and inline.children[0] is link + + +def _extract_description_html(inline: SyntaxTreeNode, first_link: SyntaxTreeNode) -> str: + """Extract description HTML from inline content after the first link. + + AST: [link("name"), text(" - Description.")] -> "Description." + The separator (- / en-dash / em-dash) is stripped. + """ + link_idx = next((i for i, c in enumerate(inline.children) if c is first_link), None) + if link_idx is None: + return "" + desc_children = inline.children[link_idx + 1 :] + if not desc_children: + return "" + html = render_inline_html(desc_children) + return _DESC_SEP_RE.sub("", html) + + +def _parse_list_entries(bullet_list: SyntaxTreeNode) -> list[ParsedEntry]: + """Extract entries from a bullet_list AST node. + + Handles three patterns: + - Text-only list_item -> subcategory label -> recurse into nested list + - Link list_item with nested link-only items -> entry with also_see + - Link list_item without nesting -> simple entry + """ + entries: list[ParsedEntry] = [] + + for list_item in bullet_list.children: + if list_item.type != "list_item": + continue + + inline = _find_inline(list_item) + if inline is None: + continue + + first_link = _find_first_link(inline) + + if first_link is None or not _is_leading_link(inline, first_link): + # Subcategory label (plain text or text-before-link) — recurse into nested list + nested = _find_child(list_item, "bullet_list") + if nested: + entries.extend(_parse_list_entries(nested)) + continue + + # Entry with a link + name = render_inline_text(first_link.children) + url = first_link.attrGet("href") or "" + desc_html = _extract_description_html(inline, first_link) + + # Collect also_see from nested bullet_list + also_see: list[AlsoSee] = [] + nested = _find_child(list_item, "bullet_list") + if nested: + for sub_item in nested.children: + if sub_item.type != "list_item": + continue + sub_inline = _find_inline(sub_item) + if sub_inline: + sub_link = _find_first_link(sub_inline) + if sub_link: + also_see.append(AlsoSee( + name=render_inline_text(sub_link.children), + url=sub_link.attrGet("href") or "", + )) + + entries.append(ParsedEntry( + name=name, + url=url, + description=desc_html, + also_see=also_see, + )) + + return entries + + +def _parse_section_entries(content_nodes: list[SyntaxTreeNode]) -> list[ParsedEntry]: + """Extract all entries from a section's content nodes.""" + entries: list[ParsedEntry] = [] + for node in content_nodes: + if node.type == "bullet_list": + entries.extend(_parse_list_entries(node)) + return entries + + +# --- Content HTML rendering -------------------------------------------------- + + +def _render_bullet_list_html( + bullet_list: SyntaxTreeNode, + *, + is_sub: bool = False, +) -> str: + """Render a bullet_list node to HTML with entry/entry-sub/subcat classes.""" + out: list[str] = [] + + for list_item in bullet_list.children: + if list_item.type != "list_item": + continue + + inline = _find_inline(list_item) + if inline is None: + continue + + first_link = _find_first_link(inline) + + if first_link is None or not _is_leading_link(inline, first_link): + # Subcategory label (plain text or text-before-link) + label = str(escape(render_inline_text(inline.children))) + out.append(f'
{label}
') + nested = _find_child(list_item, "bullet_list") + if nested: + out.append(_render_bullet_list_html(nested, is_sub=False)) + continue + + # Entry with a link + name = str(escape(render_inline_text(first_link.children))) + url = str(escape(first_link.attrGet("href") or "")) + + if is_sub: + out.append(f'
{name}
') + else: + desc = _extract_description_html(inline, first_link) + if desc: + out.append( + f'
{name}' + f'{desc}
' + ) + else: + out.append(f'
{name}
') + + # Nested items under an entry with a link are sub-entries + nested = _find_child(list_item, "bullet_list") + if nested: + out.append(_render_bullet_list_html(nested, is_sub=True)) + + return "\n".join(out) + + +def _render_section_html(content_nodes: list[SyntaxTreeNode]) -> str: + """Render a section's content nodes to HTML.""" + parts: list[str] = [] + for node in content_nodes: + if node.type == "bullet_list": + parts.append(_render_bullet_list_html(node)) + return "\n".join(parts) + + +# --- Section splitting ------------------------------------------------------- + + +def _build_section(name: str, body: list[SyntaxTreeNode]) -> ParsedSection: + """Build a ParsedSection from a heading name and its body nodes.""" + desc = _extract_description(body) + content_nodes = body[1:] if desc else body + entries = _parse_section_entries(content_nodes) + entry_count = len(entries) + sum(len(e["also_see"]) for e in entries) + preview = ", ".join(e["name"] for e in entries[:4]) + content_html = _render_section_html(content_nodes) + return ParsedSection( + name=name, + slug=slugify(name), + description=desc, + entries=entries, + entry_count=entry_count, + preview=preview, + content_html=content_html, + ) + + +def _group_by_h2( + nodes: list[SyntaxTreeNode], +) -> list[ParsedSection]: + """Group AST nodes into sections by h2 headings.""" + sections: list[ParsedSection] = [] + current_name: str | None = None + current_body: list[SyntaxTreeNode] = [] + + def flush() -> None: + nonlocal current_name + if current_name is None: + return + sections.append(_build_section(current_name, current_body)) + current_name = None + + for node in nodes: + if node.type == "heading" and node.tag == "h2": + flush() + current_name = _heading_text(node) + current_body = [] + elif current_name is not None: + current_body.append(node) + + flush() + return sections + + +def _is_bold_marker(node: SyntaxTreeNode) -> str | None: + """Detect a bold-only paragraph used as a group marker. + + Pattern: a paragraph whose only content is **Group Name** (possibly + surrounded by empty text nodes in the AST). + Returns the group name text, or None if not a group marker. + """ + if node.type != "paragraph": + return None + for child in node.children: + if child.type != "inline": + continue + # Filter out empty text nodes that markdown-it inserts around strong + meaningful = [c for c in child.children if not (c.type == "text" and c.content == "")] + if len(meaningful) == 1 and meaningful[0].type == "strong": + return render_inline_text(meaningful[0].children) + return None + + +def _parse_grouped_sections( + nodes: list[SyntaxTreeNode], +) -> list[ParsedGroup]: + """Parse nodes into groups of categories using bold markers as group boundaries. + + Bold-only paragraphs (**Group Name**) delimit groups. H2 headings under each + bold marker become categories within that group. Categories appearing before + any bold marker go into an "Other" group. + """ + groups: list[ParsedGroup] = [] + current_group_name: str | None = None + current_group_cats: list[ParsedSection] = [] + current_cat_name: str | None = None + current_cat_body: list[SyntaxTreeNode] = [] + + def flush_cat() -> None: + nonlocal current_cat_name + if current_cat_name is None: + return + current_group_cats.append(_build_section(current_cat_name, current_cat_body)) + current_cat_name = None + + def flush_group() -> None: + nonlocal current_group_name, current_group_cats + if not current_group_cats: + current_group_name = None + current_group_cats = [] + return + name = current_group_name or "Other" + groups.append(ParsedGroup( + name=name, + slug=slugify(name), + categories=list(current_group_cats), + )) + current_group_name = None + current_group_cats = [] + + for node in nodes: + bold_name = _is_bold_marker(node) + if bold_name is not None: + flush_cat() + flush_group() + current_group_name = bold_name + current_cat_body = [] + elif node.type == "heading" and node.tag == "h2": + flush_cat() + current_cat_name = _heading_text(node) + current_cat_body = [] + elif current_cat_name is not None: + current_cat_body.append(node) + + flush_cat() + flush_group() + return groups + + +def parse_readme(text: str) -> tuple[list[ParsedGroup], list[ParsedSection]]: + """Parse README.md text into grouped categories and resources. + + Returns (groups, resources) where groups is a list of ParsedGroup dicts + containing nested categories, and resources is a flat list of ParsedSection. + """ + md = MarkdownIt("commonmark") + tokens = md.parse(text) + root = SyntaxTreeNode(tokens) + children = root.children + + # Find thematic break (---), # Resources, and # Contributing in one pass + hr_idx = None + resources_idx = None + contributing_idx = None + for i, node in enumerate(children): + if hr_idx is None and node.type == "hr": + hr_idx = i + elif node.type == "heading" and node.tag == "h1": + text_content = _heading_text(node) + if text_content == "Resources": + resources_idx = i + elif text_content == "Contributing": + contributing_idx = i + if hr_idx is None: + return [], [] + + # Slice into category and resource ranges + cat_end = resources_idx or contributing_idx or len(children) + cat_nodes = children[hr_idx + 1 : cat_end] + + res_nodes: list[SyntaxTreeNode] = [] + if resources_idx is not None: + res_end = contributing_idx or len(children) + res_nodes = children[resources_idx + 1 : res_end] + + groups = _parse_grouped_sections(cat_nodes) + resources = _group_by_h2(res_nodes) + + return groups, resources diff --git a/website/static/favicon.svg b/website/static/favicon.svg new file mode 100644 index 0000000000..660a7c6f31 --- /dev/null +++ b/website/static/favicon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/website/static/main.js b/website/static/main.js new file mode 100644 index 0000000000..9c7b1595e2 --- /dev/null +++ b/website/static/main.js @@ -0,0 +1,293 @@ +// State +var activeFilter = null; // { type: "cat"|"group", value: "..." } +var activeSort = { col: 'stars', order: 'desc' }; +var searchInput = document.querySelector('.search'); +var filterBar = document.querySelector('.filter-bar'); +var filterValue = document.querySelector('.filter-value'); +var filterClear = document.querySelector('.filter-clear'); +var noResults = document.querySelector('.no-results'); +var rows = document.querySelectorAll('.table tbody tr.row'); +var tags = document.querySelectorAll('.tag'); +var tbody = document.querySelector('.table tbody'); + +// Relative time formatting +function relativeTime(isoStr) { + var date = new Date(isoStr); + var now = new Date(); + var diffMs = now - date; + var diffHours = Math.floor(diffMs / 3600000); + var diffDays = Math.floor(diffMs / 86400000); + if (diffHours < 1) return 'just now'; + if (diffHours < 24) return diffHours === 1 ? '1 hour ago' : diffHours + ' hours ago'; + if (diffDays === 1) return 'yesterday'; + if (diffDays < 30) return diffDays + ' days ago'; + var diffMonths = Math.floor(diffDays / 30); + if (diffMonths < 12) return diffMonths === 1 ? '1 month ago' : diffMonths + ' months ago'; + var diffYears = Math.floor(diffDays / 365); + return diffYears === 1 ? '1 year ago' : diffYears + ' years ago'; +} + +// Format all commit date cells +document.querySelectorAll('.col-commit[data-commit]').forEach(function (td) { + var time = td.querySelector('time'); + if (time) time.textContent = relativeTime(td.dataset.commit); +}); + +// Store original row order for sort reset +rows.forEach(function (row, i) { + row._origIndex = i; + row._expandRow = row.nextElementSibling; +}); + +function collapseAll() { + var openRows = document.querySelectorAll('.table tbody tr.row.open'); + openRows.forEach(function (row) { + row.classList.remove('open'); + row.setAttribute('aria-expanded', 'false'); + }); +} + +function applyFilters() { + var query = searchInput ? searchInput.value.toLowerCase().trim() : ''; + var visibleCount = 0; + + // Collapse all expanded rows on filter/search change + collapseAll(); + + rows.forEach(function (row) { + var show = true; + + // Category/group filter + if (activeFilter) { + var attr = activeFilter.type === 'cat' ? row.dataset.cats : row.dataset.groups; + show = attr ? attr.split('||').indexOf(activeFilter.value) !== -1 : false; + } + + // Text search + if (show && query) { + if (!row._searchText) { + var text = row.textContent.toLowerCase(); + var next = row.nextElementSibling; + if (next && next.classList.contains('expand-row')) { + text += ' ' + next.textContent.toLowerCase(); + } + row._searchText = text; + } + show = row._searchText.includes(query); + } + + if (row.hidden !== !show) row.hidden = !show; + + if (show) { + visibleCount++; + var numCell = row.cells[0]; + if (numCell.textContent !== String(visibleCount)) { + numCell.textContent = String(visibleCount); + } + } + }); + + if (noResults) noResults.hidden = visibleCount > 0; + + // Update tag highlights + tags.forEach(function (tag) { + var isActive = activeFilter + && tag.dataset.type === activeFilter.type + && tag.dataset.value === activeFilter.value; + tag.classList.toggle('active', isActive); + }); + + // Filter bar + if (filterBar) { + if (activeFilter) { + filterBar.hidden = false; + if (filterValue) filterValue.textContent = activeFilter.value; + } else { + filterBar.hidden = true; + } + } + + updateURL(); +} + +function updateURL() { + var params = new URLSearchParams(); + var query = searchInput ? searchInput.value.trim() : ''; + if (query) params.set('q', query); + if (activeFilter) { + params.set(activeFilter.type === 'cat' ? 'category' : 'group', activeFilter.value); + } + if (activeSort.col !== 'stars' || activeSort.order !== 'desc') { + params.set('sort', activeSort.col); + params.set('order', activeSort.order); + } + var qs = params.toString(); + history.replaceState(null, '', qs ? '?' + qs : location.pathname); +} + +function getSortValue(row, col) { + if (col === 'name') { + return row.querySelector('.col-name a').textContent.trim().toLowerCase(); + } + if (col === 'stars') { + var text = row.querySelector('.col-stars').textContent.trim().replace(/,/g, ''); + var num = parseInt(text, 10); + return isNaN(num) ? -1 : num; + } + if (col === 'commit-time') { + var attr = row.querySelector('.col-commit').getAttribute('data-commit'); + return attr ? new Date(attr).getTime() : 0; + } + return 0; +} + +function sortRows() { + var arr = Array.prototype.slice.call(rows); + if (activeSort) { + arr.sort(function (a, b) { + var aVal = getSortValue(a, activeSort.col); + var bVal = getSortValue(b, activeSort.col); + if (activeSort.col === 'name') { + var cmp = aVal < bVal ? -1 : aVal > bVal ? 1 : 0; + if (cmp === 0) return a._origIndex - b._origIndex; + return activeSort.order === 'desc' ? -cmp : cmp; + } + if (aVal <= 0 && bVal <= 0) return a._origIndex - b._origIndex; + if (aVal <= 0) return 1; + if (bVal <= 0) return -1; + var cmp = aVal - bVal; + if (cmp === 0) return a._origIndex - b._origIndex; + return activeSort.order === 'desc' ? -cmp : cmp; + }); + } else { + arr.sort(function (a, b) { return a._origIndex - b._origIndex; }); + } + arr.forEach(function (row) { + tbody.appendChild(row); + tbody.appendChild(row._expandRow); + }); + applyFilters(); +} + +function updateSortIndicators() { + document.querySelectorAll('th[data-sort]').forEach(function (th) { + th.classList.remove('sort-asc', 'sort-desc'); + if (activeSort && th.dataset.sort === activeSort.col) { + th.classList.add('sort-' + activeSort.order); + } + }); +} + +// Expand/collapse: event delegation on tbody +if (tbody) { + tbody.addEventListener('click', function (e) { + // Don't toggle if clicking a link or tag button + if (e.target.closest('a') || e.target.closest('.tag')) return; + + var row = e.target.closest('tr.row'); + if (!row) return; + + var isOpen = row.classList.contains('open'); + if (isOpen) { + row.classList.remove('open'); + row.setAttribute('aria-expanded', 'false'); + } else { + row.classList.add('open'); + row.setAttribute('aria-expanded', 'true'); + } + }); + + // Keyboard: Enter or Space on focused .row toggles expand + tbody.addEventListener('keydown', function (e) { + if (e.key !== 'Enter' && e.key !== ' ') return; + var row = e.target.closest('tr.row'); + if (!row) return; + e.preventDefault(); + row.click(); + }); +} + +// Tag click: filter by category or group +tags.forEach(function (tag) { + tag.addEventListener('click', function (e) { + e.preventDefault(); + var type = tag.dataset.type; + var value = tag.dataset.value; + + // Toggle: click same filter again to clear + if (activeFilter && activeFilter.type === type && activeFilter.value === value) { + activeFilter = null; + } else { + activeFilter = { type: type, value: value }; + } + applyFilters(); + }); +}); + +// Clear filter +if (filterClear) { + filterClear.addEventListener('click', function () { + activeFilter = null; + applyFilters(); + }); +} + +// Column sorting +document.querySelectorAll('th[data-sort]').forEach(function (th) { + th.addEventListener('click', function () { + var col = th.dataset.sort; + var defaultOrder = col === 'name' ? 'asc' : 'desc'; + var altOrder = defaultOrder === 'asc' ? 'desc' : 'asc'; + if (activeSort && activeSort.col === col) { + if (activeSort.order === defaultOrder) activeSort = { col: col, order: altOrder }; + else activeSort = { col: 'stars', order: 'desc' }; + } else { + activeSort = { col: col, order: defaultOrder }; + } + sortRows(); + updateSortIndicators(); + }); +}); + +// Search input +if (searchInput) { + var searchTimer; + searchInput.addEventListener('input', function () { + clearTimeout(searchTimer); + searchTimer = setTimeout(applyFilters, 150); + }); + + // Keyboard shortcuts + document.addEventListener('keydown', function (e) { + if (e.key === '/' && !['INPUT', 'TEXTAREA', 'SELECT'].includes(document.activeElement.tagName) && !e.ctrlKey && !e.metaKey) { + e.preventDefault(); + searchInput.focus(); + } + if (e.key === 'Escape' && document.activeElement === searchInput) { + searchInput.value = ''; + activeFilter = null; + applyFilters(); + searchInput.blur(); + } + }); +} + +// Restore state from URL +(function () { + var params = new URLSearchParams(location.search); + var q = params.get('q'); + var cat = params.get('category'); + var group = params.get('group'); + var sort = params.get('sort'); + var order = params.get('order'); + if (q && searchInput) searchInput.value = q; + if (cat) activeFilter = { type: 'cat', value: cat }; + else if (group) activeFilter = { type: 'group', value: group }; + if ((sort === 'name' || sort === 'stars' || sort === 'commit-time') && (order === 'desc' || order === 'asc')) { + activeSort = { col: sort, order: order }; + } + if (q || cat || group || sort) { + sortRows(); + } + updateSortIndicators(); +})(); diff --git a/website/static/style.css b/website/static/style.css new file mode 100644 index 0000000000..8f86f881d2 --- /dev/null +++ b/website/static/style.css @@ -0,0 +1,606 @@ +/* === Reset & Base === */ +*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } + +:root { + --font-display: Georgia, "Noto Serif", "Times New Roman", serif; + --font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif; + + --text-xs: 0.9375rem; + --text-sm: 1rem; + --text-base: 1.125rem; + + --bg: oklch(99.5% 0.003 240); + --bg-hover: oklch(97% 0.008 240); + --text: oklch(15% 0.005 240); + --text-secondary: oklch(35% 0.005 240); + --text-muted: oklch(50% 0.005 240); + --border: oklch(90% 0.005 240); + --border-strong: oklch(75% 0.008 240); + --border-heavy: oklch(25% 0.01 240); + --bg-input: oklch(94.5% 0.035 240); + --accent: oklch(42% 0.14 240); + --accent-hover: oklch(32% 0.16 240); + --accent-light: oklch(97% 0.015 240); + --highlight: oklch(93% 0.10 90); + --highlight-text: oklch(35% 0.10 90); + --tag-text: oklch(45% 0.06 240); + --tag-hover-bg: oklch(93% 0.025 240); +} + +html { font-size: 16px; } + +body { + font-family: var(--font-body); + background: var(--bg); + color: var(--text); + line-height: 1.55; + min-height: 100vh; + display: flex; + flex-direction: column; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +a { color: var(--accent); text-decoration: none; text-underline-offset: 0.15em; } +a:hover { color: var(--accent-hover); text-decoration: underline; } + +/* === Skip Link === */ +.skip-link { + position: absolute; + left: -9999px; + top: 0; + padding: 0.5rem 1rem; + background: var(--text); + color: var(--bg); + font-size: var(--text-xs); + font-weight: 700; + z-index: 200; +} + +.skip-link:focus { left: 0; } + +/* === Hero === */ +.hero { + max-width: 1400px; + margin: 0 auto; + padding: 3.5rem 2rem 1.5rem; +} + +.hero-main { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + align-items: flex-start; + gap: 1rem; +} + +.hero-submit { + flex-shrink: 0; + padding: 0.4rem 1rem; + border: 1px solid var(--border-strong); + border-radius: 4px; + font-size: var(--text-sm); + color: var(--text); + text-decoration: none; + white-space: nowrap; + transition: border-color 0.2s, background 0.2s, color 0.2s; +} + +.hero-submit:hover { + border-color: var(--accent); + background: var(--accent-light); + color: var(--accent); + text-decoration: none; +} + +.hero-submit:active { + transform: scale(0.97); +} + +.hero-submit:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; +} + +.hero h1 { + font-family: var(--font-display); + font-size: clamp(2rem, 5vw, 3rem); + font-weight: 400; + letter-spacing: -0.01em; + line-height: 1.1; + text-wrap: balance; + color: var(--accent); + margin-bottom: 0.75rem; +} + +.hero-sub { + font-size: var(--text-base); + color: var(--text-secondary); + line-height: 1.6; + margin-bottom: 0.5rem; + text-wrap: pretty; +} + +.hero-sub a { color: var(--text-secondary); font-weight: 600; } +.hero-sub a:hover { color: var(--accent); } + +.hero-gh { + font-size: var(--text-sm); + color: var(--text-muted); + font-weight: 500; +} + +.hero-gh:hover { color: var(--accent); } + +/* === Controls === */ +.controls { + max-width: 1400px; + margin: 0 auto; + padding: 0 2rem 1rem; +} + +.search-wrap { + position: relative; + margin-bottom: 0.75rem; +} + +.search-icon { + position: absolute; + left: 1rem; + top: 50%; + transform: translateY(-50%); + color: var(--text-muted); + pointer-events: none; +} + +.search { + width: 100%; + padding: 0.65rem 1rem 0.65rem 2.75rem; + border: 1px solid transparent; + border-radius: 4px; + background: var(--bg-input); + font-family: var(--font-body); + font-size: var(--text-sm); + color: var(--text); + transition: border-color 0.15s, background 0.15s; +} + +.search::placeholder { color: var(--text-muted); } + +.search:focus { + outline: 2px solid var(--accent); + outline-offset: 2px; + border-color: var(--accent); + background: var(--bg); +} + +.filter-bar[hidden] { display: none; } + +.filter-bar { + display: flex; + align-items: center; + gap: 0.75rem; + padding: 0.5rem 0; + font-size: var(--text-sm); + color: var(--text-secondary); +} + +.filter-bar strong { + color: var(--text); +} + +.filter-clear { + background: none; + border: 1px solid var(--border); + border-radius: 4px; + padding: 0.35rem 0.65rem; + font-family: inherit; + font-size: var(--text-xs); + color: var(--text-muted); + cursor: pointer; + transition: border-color 0.15s, color 0.15s; +} + +.filter-clear:active { + transform: scale(0.97); +} + +.filter-clear:hover { + border-color: var(--text-muted); + color: var(--text); +} + +.filter-clear:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; +} + +/* === Table === */ +.table-wrap { + width: 100%; + padding: 0; + overflow-x: auto; +} + +.table-wrap:focus { + outline: 2px solid var(--accent); + outline-offset: -2px; +} + +.table { + width: 100%; + border-collapse: separate; + border-spacing: 0; + font-size: var(--text-sm); +} + +.table thead th { + text-align: left; + font-weight: 700; + font-size: var(--text-base); + color: var(--text); + padding: 0.65rem 0.75rem; + border-bottom: 2px solid var(--border-heavy); + position: sticky; + top: 0; + background: var(--bg); + z-index: 10; + white-space: nowrap; +} + +.table thead th:first-child, +.table tbody td:first-child { + padding-left: max(2rem, calc(50vw - 700px + 2rem)); +} + +.table thead th:last-child, +.table tbody td:last-child { + padding-right: max(2rem, calc(50vw - 700px + 2rem)); +} + +.table tbody td { + padding: 0.7rem 0.75rem; + border-bottom: 1px solid var(--border); + vertical-align: top; + transition: background 0.15s; +} + +.table tbody tr.row:not(.open):hover td { + background: var(--bg-hover); +} + +.table tbody tr[hidden] { display: none; } + +.col-num { + width: 3rem; + color: var(--text-muted); + font-variant-numeric: tabular-nums; + text-align: left; +} + +.col-name { + width: 30%; + overflow-wrap: anywhere; +} + +.col-name > a { + font-weight: 500; + color: var(--accent); + text-decoration: none; +} + +.col-name > a:hover { text-decoration: underline; color: var(--accent-hover); } + +/* === Sortable Headers === */ +th[data-sort] { + cursor: pointer; + user-select: none; +} + +th[data-sort]:hover { + color: var(--accent); +} + +th[data-sort]::after { + content: " ▼"; + opacity: 0; + transition: opacity 0.15s; +} + +th[data-sort="name"]::after { + content: " ▲"; +} + +th[data-sort]:hover::after { + opacity: 1; +} + +th[data-sort].sort-desc::after { + content: " ▼"; + opacity: 1; +} + +th[data-sort].sort-asc::after { + content: " ▲"; + opacity: 1; +} + +/* === Stars Column === */ +.col-stars { + width: 5rem; + font-variant-numeric: tabular-nums; + white-space: nowrap; + color: var(--text-secondary); + text-align: right; +} + +/* === Arrow Column === */ +.col-arrow { + width: 2.5rem; + text-align: center; +} + +.arrow { + display: inline-block; + font-size: 0.8rem; + color: var(--accent); + transition: transform 0.15s ease; +} + +.row.open .arrow { + transform: rotate(90deg); +} + +/* === Row Click === */ +.row { cursor: pointer; } +.row:active td { background: var(--bg-hover); } + +.row:focus-visible td { + outline: none; + background: var(--bg-hover); + box-shadow: inset 2px 0 0 var(--accent); +} + +/* === Expand Row === */ +.expand-row { + display: none; +} + +.row.open + .expand-row { + display: table-row; +} + +.row.open td { + background: var(--accent-light); + border-bottom-color: transparent; + padding-bottom: 0.1rem; +} + +.expand-row td { + padding: 0.15rem 0.75rem 0.75rem; + background: var(--accent-light); + border-bottom: 1px solid var(--border); +} + +@keyframes expand-in { + from { + opacity: 0; + transform: translateY(-4px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.expand-content { + font-size: var(--text-sm); + color: var(--text-secondary); + line-height: 1.6; + text-wrap: pretty; + animation: expand-in 0.2s cubic-bezier(0.25, 1, 0.5, 1); +} + +.expand-tags { + display: flex; + gap: 0.4rem; + margin-bottom: 0.4rem; +} + +.expand-tag { + font-size: var(--text-xs); + color: var(--tag-text); + background: var(--bg); + padding: 0.15rem 0.4rem; + border-radius: 3px; +} + +.expand-also-see { + margin-top: 0.25rem; + font-size: var(--text-xs); + color: var(--text-muted); +} + +.expand-also-see a { + color: var(--accent); + text-decoration: none; +} + +.expand-also-see a:hover { + text-decoration: underline; +} + +.expand-meta { + margin-top: 0.25rem; + font-size: var(--text-xs); + color: var(--text-muted); + font-weight: normal; +} + +.expand-meta a { + color: var(--accent); + text-decoration: none; +} + +.expand-meta a:hover { + text-decoration: underline; +} + +.expand-sep { + margin: 0 0.25rem; + color: var(--border); +} + +.col-cat { + white-space: nowrap; +} + +.col-cat .tag + .tag { + margin-left: 0.35rem; +} + +/* === Last Commit Column === */ +.col-commit { + width: 9rem; + white-space: nowrap; + color: var(--text-muted); +} + +/* === Tags === */ +.tag { + position: relative; + background: var(--accent-light); + border: none; + font-family: inherit; + font-size: var(--text-xs); + color: var(--tag-text); + cursor: pointer; + padding: 0.25rem 0.5rem; + border-radius: 3px; + white-space: nowrap; + transition: background 0.15s, color 0.15s; +} + +/* Expand touch target to 44x44px minimum */ +.tag::after { + content: ""; + position: absolute; + inset: -0.5rem -0.25rem; +} + +.tag:active { + transform: scale(0.95); +} + +.tag:hover { + background: var(--tag-hover-bg); + color: var(--accent); +} + +.tag:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 1px; +} + +.tag.active { + background: var(--highlight); + color: var(--highlight-text); + font-weight: 600; +} + +/* === Noscript === */ +.noscript-msg { + text-align: center; + padding: 1rem; + color: var(--text-muted); +} + +/* === No Results === */ +.no-results { + max-width: 1400px; + margin: 0 auto; + padding: 3rem 2rem; + font-size: var(--text-base); + color: var(--text-muted); + text-align: center; +} + +/* === Footer === */ +.footer { + margin-top: auto; + border-top: none; + width: 100%; + padding: 1.25rem 2rem; + font-size: var(--text-xs); + color: var(--text-muted); + background: var(--bg-input); + display: flex; + align-items: center; + justify-content: flex-end; + gap: 0.5rem; +} + +.footer a { color: var(--accent); text-decoration: none; } +.footer a:hover { color: var(--accent-hover); text-decoration: underline; } + +.footer-sep { color: var(--border-strong); } + +/* === Responsive === */ +@media (max-width: 900px) { + .col-commit { display: none; } + .tag-group { display: none; } + .col-name { width: 50%; } +} + +@media (max-width: 640px) { + .hero { padding: 2rem 1.25rem 1rem; } + .controls { padding: 0 1.25rem 0.75rem; } + + .table { table-layout: auto; } + + .table thead th, + .table tbody td { + padding-left: 0.5rem; + padding-right: 0.5rem; + } + + .table thead th:first-child, + .table tbody td:first-child { padding-left: 0.25rem; } + + .table thead th:last-child, + .table tbody td:last-child { padding-right: 0.25rem; } + + .table thead th { font-size: var(--text-sm); } + + .col-num { width: 2rem; } + .col-stars { width: 4.75rem; } + .col-arrow { width: 1.25rem; } + .col-cat { display: none; } + .col-name { + width: auto; + white-space: normal; + } + .footer { padding: 1.25rem; justify-content: center; flex-wrap: wrap; } +} + +/* === Screen Reader Only === */ +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +/* === Reduced Motion === */ +@media (prefers-reduced-motion: reduce) { + *, *::before, *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } +} diff --git a/website/templates/base.html b/website/templates/base.html new file mode 100644 index 0000000000..ea7c7e5210 --- /dev/null +++ b/website/templates/base.html @@ -0,0 +1,64 @@ + + + + + + {% block title %}Awesome Python{% endblock %} + + + + + + + + + + + + + + + +
{% block content %}{% endblock %}
+ + + + + + + diff --git a/website/templates/index.html b/website/templates/index.html new file mode 100644 index 0000000000..9e8b25d12a --- /dev/null +++ b/website/templates/index.html @@ -0,0 +1,169 @@ +{% extends "base.html" %} {% block content %} +
+
+
+

Awesome Python

+

+ {{ subtitle }}
Maintained by + @vinta + and + @JinyangWang27. +

+ awesome-python on GitHub → +
+ Submit a Project +
+
+ +

Search and filter

+
+
+ + + + + +
+ +
+ +

Results

+
+ + + + + + + + + + + + + {% for entry in entries %} + + + + + + + + + + + + + + {% endfor %} + +
#Project NameGitHub StarsLast CommitCategoryDetails
+
+ {% if entry.description %} +
{{ entry.description | safe }}
+ {% endif %} {% if entry.also_see %} +
+ Also see: {% for see in entry.also_see %}{{ see.name }}{% if not loop.last %}, {% endif %}{% endfor %} +
+ {% endif %} + +
+
+
+ + +{% endblock %} diff --git a/website/tests/test_build.py b/website/tests/test_build.py new file mode 100644 index 0000000000..6302c3d3e3 --- /dev/null +++ b/website/tests/test_build.py @@ -0,0 +1,367 @@ +"""Tests for the build module.""" + +import json +import shutil +import textwrap +from pathlib import Path + +from build import ( + build, + extract_github_repo, + group_categories, + load_stars, + sort_entries, +) +from readme_parser import slugify + +# --------------------------------------------------------------------------- +# slugify +# --------------------------------------------------------------------------- + + +class TestSlugify: + def test_simple(self): + assert slugify("Admin Panels") == "admin-panels" + + def test_uppercase_acronym(self): + assert slugify("RESTful API") == "restful-api" + + def test_all_caps(self): + assert slugify("CMS") == "cms" + + def test_hyphenated_input(self): + assert slugify("Command-line Tools") == "command-line-tools" + + def test_special_chars(self): + assert slugify("Editor Plugins and IDEs") == "editor-plugins-and-ides" + + def test_single_word(self): + assert slugify("Audio") == "audio" + + def test_extra_spaces(self): + assert slugify(" Date and Time ") == "date-and-time" + + +# --------------------------------------------------------------------------- +# group_categories +# --------------------------------------------------------------------------- + + +class TestGroupCategories: + def test_appends_resources(self): + parsed_groups = [ + {"name": "G1", "slug": "g1", "categories": [{"name": "Cat1"}]}, + ] + resources = [{"name": "Newsletters", "slug": "newsletters"}] + groups = group_categories(parsed_groups, resources) + group_names = [g["name"] for g in groups] + assert "G1" in group_names + assert "Resources" in group_names + + def test_no_resources_no_extra_group(self): + parsed_groups = [ + {"name": "G1", "slug": "g1", "categories": [{"name": "Cat1"}]}, + ] + groups = group_categories(parsed_groups, []) + assert len(groups) == 1 + assert groups[0]["name"] == "G1" + + def test_preserves_group_order(self): + parsed_groups = [ + {"name": "Second", "slug": "second", "categories": [{"name": "C2"}]}, + {"name": "First", "slug": "first", "categories": [{"name": "C1"}]}, + ] + groups = group_categories(parsed_groups, []) + assert groups[0]["name"] == "Second" + assert groups[1]["name"] == "First" + + +# --------------------------------------------------------------------------- +# build (integration) +# --------------------------------------------------------------------------- + + +class TestBuild: + def _make_repo(self, tmp_path, readme): + (tmp_path / "README.md").write_text(readme, encoding="utf-8") + tpl_dir = tmp_path / "website" / "templates" + tpl_dir.mkdir(parents=True) + (tpl_dir / "base.html").write_text( + "{% block title %}{% endblock %}" + "" + "{% block content %}{% endblock %}", + encoding="utf-8", + ) + (tpl_dir / "index.html").write_text( + '{% extends "base.html" %}{% block content %}' + "{% for group in groups %}" + '
' + "

{{ group.name }}

" + "{% for cat in group.categories %}" + '
' + "{{ cat.name }}" + "{{ cat.preview }}" + "{{ cat.entry_count }}" + '' + "
" + "{% endfor %}" + "
" + "{% endfor %}" + "{% endblock %}", + encoding="utf-8", + ) + + def test_build_creates_single_page(self, tmp_path): + readme = textwrap.dedent("""\ + # Awesome Python + + Intro. + + --- + + **Tools** + + ## Widgets + + _Widget libraries._ + + - [w1](https://example.com) - A widget. + + ## Gadgets + + _Gadget tools._ + + - [g1](https://example.com) - A gadget. + + # Resources + + Info. + + ## Newsletters + + - [NL](https://example.com) + + # Contributing + + Help! + """) + self._make_repo(tmp_path, readme) + build(str(tmp_path)) + + site = tmp_path / "website" / "output" + assert (site / "index.html").exists() + # No category sub-pages + assert not (site / "categories").exists() + + def test_build_cleans_stale_output(self, tmp_path): + readme = textwrap.dedent("""\ + # T + + --- + + ## Only + + - [x](https://x.com) - X. + + # Contributing + + Done. + """) + self._make_repo(tmp_path, readme) + + stale = tmp_path / "website" / "output" / "categories" / "stale" + stale.mkdir(parents=True) + (stale / "index.html").write_text("old", encoding="utf-8") + + build(str(tmp_path)) + + assert not (tmp_path / "website" / "output" / "categories" / "stale").exists() + + def test_index_contains_category_names(self, tmp_path): + readme = textwrap.dedent("""\ + # T + + --- + + **Group A** + + ## Alpha + + - [a](https://x.com) - A. + + **Group B** + + ## Beta + + - [b](https://x.com) - B. + + # Contributing + + Done. + """) + self._make_repo(tmp_path, readme) + build(str(tmp_path)) + + index_html = (tmp_path / "website" / "output" / "index.html").read_text() + assert "Alpha" in index_html + assert "Beta" in index_html + assert "Group A" in index_html + assert "Group B" in index_html + + def test_index_contains_preview_text(self, tmp_path): + readme = textwrap.dedent("""\ + # T + + --- + + ## Stuff + + - [django](https://x.com) - A framework. + - [flask](https://x.com) - A micro. + + # Contributing + + Done. + """) + self._make_repo(tmp_path, readme) + build(str(tmp_path)) + + index_html = (tmp_path / "website" / "output" / "index.html").read_text() + assert "django" in index_html + assert "flask" in index_html + + def test_build_with_stars_sorts_by_stars(self, tmp_path): + readme = textwrap.dedent("""\ + # T + + --- + + ## Stuff + + - [low-stars](https://github.com/org/low) - Low. + - [high-stars](https://github.com/org/high) - High. + - [no-stars](https://example.com/none) - None. + + # Contributing + + Done. + """) + (tmp_path / "README.md").write_text(readme, encoding="utf-8") + + # Copy real templates + real_tpl = Path(__file__).parent / ".." / "templates" + tpl_dir = tmp_path / "website" / "templates" + shutil.copytree(real_tpl, tpl_dir) + + # Create mock star data + data_dir = tmp_path / "website" / "data" + data_dir.mkdir(parents=True) + stars = { + "org/high": {"stars": 5000, "owner": "org", "fetched_at": "2026-01-01T00:00:00+00:00"}, + "org/low": {"stars": 100, "owner": "org", "fetched_at": "2026-01-01T00:00:00+00:00"}, + } + (data_dir / "github_stars.json").write_text(json.dumps(stars), encoding="utf-8") + + build(str(tmp_path)) + + html = (tmp_path / "website" / "output" / "index.html").read_text(encoding="utf-8") + # Star-sorted: high-stars (5000) before low-stars (100) before no-stars (None) + assert html.index("high-stars") < html.index("low-stars") + assert html.index("low-stars") < html.index("no-stars") + # Formatted star counts + assert "5,000" in html + assert "100" in html + # Expand content present + assert "expand-content" in html + + +# --------------------------------------------------------------------------- +# extract_github_repo +# --------------------------------------------------------------------------- + + +class TestExtractGithubRepo: + def test_github_url(self): + assert extract_github_repo("https://github.com/psf/requests") == "psf/requests" + + def test_non_github_url(self): + assert extract_github_repo("https://foss.heptapod.net/pypy/pypy") is None + + def test_github_io_url(self): + assert extract_github_repo("https://user.github.io/proj") is None + + def test_trailing_slash(self): + assert extract_github_repo("https://github.com/org/repo/") == "org/repo" + + def test_deep_path(self): + assert extract_github_repo("https://github.com/org/repo/tree/main") is None + + def test_dot_git_suffix(self): + assert extract_github_repo("https://github.com/org/repo.git") == "org/repo" + + def test_org_only(self): + assert extract_github_repo("https://github.com/org") is None + + +# --------------------------------------------------------------------------- +# load_stars +# --------------------------------------------------------------------------- + + +class TestLoadStars: + def test_returns_empty_when_missing(self, tmp_path): + result = load_stars(tmp_path / "nonexistent.json") + assert result == {} + + def test_loads_valid_json(self, tmp_path): + data = {"psf/requests": {"stars": 52467, "owner": "psf", "fetched_at": "2026-01-01T00:00:00+00:00"}} + f = tmp_path / "stars.json" + f.write_text(json.dumps(data), encoding="utf-8") + result = load_stars(f) + assert result["psf/requests"]["stars"] == 52467 + + def test_returns_empty_on_corrupt_json(self, tmp_path): + f = tmp_path / "stars.json" + f.write_text("not json", encoding="utf-8") + result = load_stars(f) + assert result == {} + + +# --------------------------------------------------------------------------- +# sort_entries +# --------------------------------------------------------------------------- + + +class TestSortEntries: + def test_sorts_by_stars_descending(self): + entries = [ + {"name": "a", "stars": 100, "url": ""}, + {"name": "b", "stars": 500, "url": ""}, + {"name": "c", "stars": 200, "url": ""}, + ] + result = sort_entries(entries) + assert [e["name"] for e in result] == ["b", "c", "a"] + + def test_equal_stars_sorted_alphabetically(self): + entries = [ + {"name": "beta", "stars": 100, "url": ""}, + {"name": "alpha", "stars": 100, "url": ""}, + ] + result = sort_entries(entries) + assert [e["name"] for e in result] == ["alpha", "beta"] + + def test_no_stars_go_to_bottom(self): + entries = [ + {"name": "no-stars", "stars": None, "url": ""}, + {"name": "has-stars", "stars": 50, "url": ""}, + ] + result = sort_entries(entries) + assert [e["name"] for e in result] == ["has-stars", "no-stars"] + + def test_no_stars_sorted_alphabetically(self): + entries = [ + {"name": "zebra", "stars": None, "url": ""}, + {"name": "apple", "stars": None, "url": ""}, + ] + result = sort_entries(entries) + assert [e["name"] for e in result] == ["apple", "zebra"] diff --git a/website/tests/test_fetch_github_stars.py b/website/tests/test_fetch_github_stars.py new file mode 100644 index 0000000000..6c9eb383d6 --- /dev/null +++ b/website/tests/test_fetch_github_stars.py @@ -0,0 +1,231 @@ +"""Tests for fetch_github_stars module.""" + +import json +import os +import sys + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) +from fetch_github_stars import ( + build_graphql_query, + extract_github_repos, + parse_graphql_response, + save_cache, +) + + +class TestExtractGithubRepos: + def test_extracts_owner_repo_from_github_url(self): + readme = "* [requests](https://github.com/psf/requests) - HTTP lib." + result = extract_github_repos(readme) + assert result == {"psf/requests"} + + def test_multiple_repos(self): + readme = ( + "* [requests](https://github.com/psf/requests) - HTTP.\n" + "* [flask](https://github.com/pallets/flask) - Micro." + ) + result = extract_github_repos(readme) + assert result == {"psf/requests", "pallets/flask"} + + def test_ignores_non_github_urls(self): + readme = "* [pypy](https://foss.heptapod.net/pypy/pypy) - Fast Python." + result = extract_github_repos(readme) + assert result == set() + + def test_ignores_github_io_urls(self): + readme = "* [docs](https://user.github.io/project) - Docs site." + result = extract_github_repos(readme) + assert result == set() + + def test_ignores_github_wiki_and_blob_urls(self): + readme = ( + "* [wiki](https://github.com/org/repo/wiki) - Wiki.\n" + "* [file](https://github.com/org/repo/blob/main/f.py) - File." + ) + result = extract_github_repos(readme) + assert result == set() + + def test_handles_trailing_slash(self): + readme = "* [lib](https://github.com/org/repo/) - Lib." + result = extract_github_repos(readme) + assert result == {"org/repo"} + + def test_deduplicates(self): + readme = ( + "* [a](https://github.com/org/repo) - A.\n" + "* [b](https://github.com/org/repo) - B." + ) + result = extract_github_repos(readme) + assert result == {"org/repo"} + + def test_strips_fragment(self): + readme = "* [lib](https://github.com/org/repo#section) - Lib." + result = extract_github_repos(readme) + assert result == {"org/repo"} + + +class TestSaveCache: + def test_creates_directory_and_writes_json(self, tmp_path, monkeypatch): + data_dir = tmp_path / "data" + cache_file = data_dir / "stars.json" + monkeypatch.setattr("fetch_github_stars.DATA_DIR", data_dir) + monkeypatch.setattr("fetch_github_stars.CACHE_FILE", cache_file) + save_cache({"a/b": {"stars": 1}}) + assert cache_file.exists() + assert json.loads(cache_file.read_text(encoding="utf-8")) == {"a/b": {"stars": 1}} + + +class TestBuildGraphqlQuery: + def test_single_repo(self): + query = build_graphql_query(["psf/requests"]) + assert "repository" in query + assert 'owner: "psf"' in query + assert 'name: "requests"' in query + assert "stargazerCount" in query + + def test_multiple_repos_use_aliases(self): + query = build_graphql_query(["psf/requests", "pallets/flask"]) + assert "repo_0:" in query + assert "repo_1:" in query + + def test_empty_list(self): + query = build_graphql_query([]) + assert query == "" + + def test_skips_repos_with_quotes_in_name(self): + query = build_graphql_query(['org/"bad"']) + assert query == "" + + def test_skips_only_bad_repos(self): + query = build_graphql_query(["good/repo", 'bad/"repo"']) + assert "good" in query + assert "bad" not in query + + +class TestParseGraphqlResponse: + def test_parses_star_count_and_owner(self): + data = { + "repo_0": { + "stargazerCount": 52467, + "owner": {"login": "psf"}, + } + } + repos = ["psf/requests"] + result = parse_graphql_response(data, repos) + assert result["psf/requests"]["stars"] == 52467 + assert result["psf/requests"]["owner"] == "psf" + + def test_skips_null_repos(self): + data = {"repo_0": None} + repos = ["deleted/repo"] + result = parse_graphql_response(data, repos) + assert result == {} + + def test_handles_missing_owner(self): + data = {"repo_0": {"stargazerCount": 100}} + repos = ["org/repo"] + result = parse_graphql_response(data, repos) + assert result["org/repo"]["owner"] == "" + + def test_multiple_repos(self): + data = { + "repo_0": {"stargazerCount": 100, "owner": {"login": "a"}}, + "repo_1": {"stargazerCount": 200, "owner": {"login": "b"}}, + } + repos = ["a/x", "b/y"] + result = parse_graphql_response(data, repos) + assert len(result) == 2 + assert result["a/x"]["stars"] == 100 + assert result["b/y"]["stars"] == 200 + + +class TestMainSkipsFreshCache: + """Verify that main() skips fetching when all cache entries are fresh.""" + + def test_skips_fetch_when_cache_is_fresh(self, tmp_path, monkeypatch, capsys): + from datetime import datetime, timedelta, timezone + + from fetch_github_stars import main + + # Set up a minimal README with one repo + readme = tmp_path / "README.md" + readme.write_text("* [req](https://github.com/psf/requests) - HTTP.\n") + monkeypatch.setattr("fetch_github_stars.README_PATH", readme) + + # Pre-populate cache with a fresh entry (1 hour ago) + data_dir = tmp_path / "data" + data_dir.mkdir() + cache_file = data_dir / "github_stars.json" + now = datetime.now(timezone.utc) + fresh_cache = { + "psf/requests": { + "stars": 52000, + "owner": "psf", + "last_commit_at": "2025-01-01T00:00:00+00:00", + "fetched_at": (now - timedelta(hours=1)).isoformat(), + } + } + cache_file.write_text(json.dumps(fresh_cache), encoding="utf-8") + monkeypatch.setattr("fetch_github_stars.CACHE_FILE", cache_file) + monkeypatch.setattr("fetch_github_stars.DATA_DIR", data_dir) + monkeypatch.setenv("GITHUB_TOKEN", "fake-token") + + main() + + output = capsys.readouterr().out + assert "0 repos to fetch" in output + assert "Cache is up to date" in output + + def test_fetches_when_cache_is_stale(self, tmp_path, monkeypatch, capsys): + from datetime import datetime, timedelta, timezone + from unittest.mock import MagicMock + + from fetch_github_stars import main + + # Set up a minimal README with one repo + readme = tmp_path / "README.md" + readme.write_text("* [req](https://github.com/psf/requests) - HTTP.\n") + monkeypatch.setattr("fetch_github_stars.README_PATH", readme) + + # Pre-populate cache with a stale entry (24 hours ago) + data_dir = tmp_path / "data" + data_dir.mkdir() + cache_file = data_dir / "github_stars.json" + now = datetime.now(timezone.utc) + stale_cache = { + "psf/requests": { + "stars": 52000, + "owner": "psf", + "last_commit_at": "2025-01-01T00:00:00+00:00", + "fetched_at": (now - timedelta(hours=24)).isoformat(), + } + } + cache_file.write_text(json.dumps(stale_cache), encoding="utf-8") + monkeypatch.setattr("fetch_github_stars.CACHE_FILE", cache_file) + monkeypatch.setattr("fetch_github_stars.DATA_DIR", data_dir) + monkeypatch.setenv("GITHUB_TOKEN", "fake-token") + + # Mock httpx.Client to avoid real API calls + mock_response = MagicMock() + mock_response.json.return_value = { + "data": { + "repo_0": { + "stargazerCount": 53000, + "owner": {"login": "psf"}, + "defaultBranchRef": {"target": {"committedDate": "2025-06-01T00:00:00Z"}}, + } + } + } + mock_response.raise_for_status = MagicMock() + mock_client = MagicMock() + mock_client.__enter__ = MagicMock(return_value=mock_client) + mock_client.__exit__ = MagicMock(return_value=False) + mock_client.post.return_value = mock_response + monkeypatch.setattr("fetch_github_stars.httpx.Client", lambda **kwargs: mock_client) + + main() + + output = capsys.readouterr().out + assert "1 repos to fetch" in output + assert "Done. Fetched 1 repos" in output + mock_client.post.assert_called_once() diff --git a/website/tests/test_readme_parser.py b/website/tests/test_readme_parser.py new file mode 100644 index 0000000000..0b0236c719 --- /dev/null +++ b/website/tests/test_readme_parser.py @@ -0,0 +1,589 @@ +"""Tests for the readme_parser module.""" + +import os +import textwrap + +import pytest + +from readme_parser import ( + _parse_section_entries, + _render_section_html, + parse_readme, + render_inline_html, + render_inline_text, +) + +from markdown_it import MarkdownIt +from markdown_it.tree import SyntaxTreeNode + + +def _parse_inline(md_text: str) -> list[SyntaxTreeNode]: + """Helper: parse a single paragraph and return its inline children.""" + md = MarkdownIt("commonmark") + root = SyntaxTreeNode(md.parse(md_text)) + # root > paragraph > inline > children + return root.children[0].children[0].children + + +class TestRenderInlineHtml: + def test_plain_text_escapes_html(self): + children = _parse_inline("Hello & friends") + assert render_inline_html(children) == "Hello <world> & friends" + + def test_link_with_target(self): + children = _parse_inline("[name](https://example.com)") + html = render_inline_html(children) + assert 'href="https://example.com"' in html + assert 'target="_blank"' in html + assert 'rel="noopener"' in html + assert ">name" in html + + def test_emphasis(self): + children = _parse_inline("*italic* text") + assert "italic" in render_inline_html(children) + + def test_strong(self): + children = _parse_inline("**bold** text") + assert "bold" in render_inline_html(children) + + def test_code_inline(self): + children = _parse_inline("`some code`") + assert "some code" in render_inline_html(children) + + def test_mixed_link_and_text(self): + children = _parse_inline("See [foo](https://x.com) for details.") + html = render_inline_html(children) + assert "See " in html + assert ">foo" in html + assert " for details." in html + + +class TestRenderInlineText: + def test_plain_text(self): + children = _parse_inline("Hello world") + assert render_inline_text(children) == "Hello world" + + def test_link_becomes_text(self): + children = _parse_inline("See [awesome-algos](https://github.com/x/y).") + assert render_inline_text(children) == "See awesome-algos." + + def test_emphasis_stripped(self): + children = _parse_inline("*italic* text") + assert render_inline_text(children) == "italic text" + + def test_code_inline_kept(self): + children = _parse_inline("`code` here") + assert render_inline_text(children) == "code here" + + +MINIMAL_README = textwrap.dedent("""\ + # Awesome Python + + Some intro text. + + --- + + ## Alpha + + _Libraries for alpha stuff._ + + - [lib-a](https://example.com/a) - Does A. + - [lib-b](https://example.com/b) - Does B. + + ## Beta + + _Tools for beta._ + + - [lib-c](https://example.com/c) - Does C. + + # Resources + + Where to discover resources. + + ## Newsletters + + - [News One](https://example.com/n1) + - [News Two](https://example.com/n2) + + ## Podcasts + + - [Pod One](https://example.com/p1) + + # Contributing + + Please contribute! +""") + + +GROUPED_README = textwrap.dedent("""\ + # Awesome Python + + Some intro text. + + --- + + **Group One** + + ## Alpha + + _Libraries for alpha stuff._ + + - [lib-a](https://example.com/a) - Does A. + - [lib-b](https://example.com/b) - Does B. + + **Group Two** + + ## Beta + + _Tools for beta._ + + - [lib-c](https://example.com/c) - Does C. + + ## Gamma + + - [lib-d](https://example.com/d) - Does D. + + # Resources + + Where to discover resources. + + ## Newsletters + + - [News One](https://example.com/n1) + + # Contributing + + Please contribute! +""") + + +class TestParseReadmeSections: + def test_ungrouped_categories_go_to_other(self): + groups, resources = parse_readme(MINIMAL_README) + assert len(groups) == 1 + assert groups[0]["name"] == "Other" + assert len(groups[0]["categories"]) == 2 + + def test_ungrouped_category_names(self): + groups, _ = parse_readme(MINIMAL_README) + cats = groups[0]["categories"] + assert cats[0]["name"] == "Alpha" + assert cats[1]["name"] == "Beta" + + def test_resource_count(self): + _, resources = parse_readme(MINIMAL_README) + assert len(resources) == 2 + + def test_category_slugs(self): + groups, _ = parse_readme(MINIMAL_README) + cats = groups[0]["categories"] + assert cats[0]["slug"] == "alpha" + assert cats[1]["slug"] == "beta" + + def test_category_description(self): + groups, _ = parse_readme(MINIMAL_README) + cats = groups[0]["categories"] + assert cats[0]["description"] == "Libraries for alpha stuff." + assert cats[1]["description"] == "Tools for beta." + + def test_resource_names(self): + _, resources = parse_readme(MINIMAL_README) + assert resources[0]["name"] == "Newsletters" + assert resources[1]["name"] == "Podcasts" + + def test_contributing_skipped(self): + groups, resources = parse_readme(MINIMAL_README) + all_names = [] + for g in groups: + all_names.extend(c["name"] for c in g["categories"]) + all_names.extend(r["name"] for r in resources) + assert "Contributing" not in all_names + + def test_no_separator(self): + groups, resources = parse_readme("# Just a heading\n\nSome text.\n") + assert groups == [] + assert resources == [] + + def test_no_description(self): + readme = textwrap.dedent("""\ + # Title + + --- + + ## NullDesc + + - [item](https://x.com) - Thing. + + # Resources + + ## Tips + + - [tip](https://x.com) + + # Contributing + + Done. + """) + groups, resources = parse_readme(readme) + cats = groups[0]["categories"] + assert cats[0]["description"] == "" + assert cats[0]["entries"][0]["name"] == "item" + + def test_description_with_link_stripped(self): + readme = textwrap.dedent("""\ + # T + + --- + + ## Algos + + _Algorithms. Also see [awesome-algos](https://example.com)._ + + - [lib](https://x.com) - Lib. + + # Contributing + + Done. + """) + groups, _ = parse_readme(readme) + cats = groups[0]["categories"] + assert cats[0]["description"] == "Algorithms. Also see awesome-algos." + + +class TestParseGroupedReadme: + def test_group_count(self): + groups, _ = parse_readme(GROUPED_README) + assert len(groups) == 2 + + def test_group_names(self): + groups, _ = parse_readme(GROUPED_README) + assert groups[0]["name"] == "Group One" + assert groups[1]["name"] == "Group Two" + + def test_group_slugs(self): + groups, _ = parse_readme(GROUPED_README) + assert groups[0]["slug"] == "group-one" + assert groups[1]["slug"] == "group-two" + + def test_group_one_has_one_category(self): + groups, _ = parse_readme(GROUPED_README) + assert len(groups[0]["categories"]) == 1 + assert groups[0]["categories"][0]["name"] == "Alpha" + + def test_group_two_has_two_categories(self): + groups, _ = parse_readme(GROUPED_README) + assert len(groups[1]["categories"]) == 2 + assert groups[1]["categories"][0]["name"] == "Beta" + assert groups[1]["categories"][1]["name"] == "Gamma" + + def test_resources_still_parsed(self): + _, resources = parse_readme(GROUPED_README) + assert len(resources) == 1 + assert resources[0]["name"] == "Newsletters" + + def test_empty_group_skipped(self): + readme = textwrap.dedent("""\ + # T + + --- + + **Empty** + + **HasCats** + + ## Cat + + - [x](https://x.com) - X. + + # Contributing + + Done. + """) + groups, _ = parse_readme(readme) + assert len(groups) == 1 + assert groups[0]["name"] == "HasCats" + + def test_bold_with_extra_text_not_group_marker(self): + readme = textwrap.dedent("""\ + # T + + --- + + **Note:** This is not a group marker. + + ## Cat + + - [x](https://x.com) - X. + + # Contributing + + Done. + """) + groups, _ = parse_readme(readme) + # "Note:" has text after the strong node, so it's not a group marker + # Category goes into "Other" + assert len(groups) == 1 + assert groups[0]["name"] == "Other" + + def test_categories_before_any_group_marker(self): + readme = textwrap.dedent("""\ + # T + + --- + + ## Orphan + + - [x](https://x.com) - X. + + **A Group** + + ## Grouped + + - [y](https://x.com) - Y. + + # Contributing + + Done. + """) + groups, _ = parse_readme(readme) + assert len(groups) == 2 + assert groups[0]["name"] == "Other" + assert groups[0]["categories"][0]["name"] == "Orphan" + assert groups[1]["name"] == "A Group" + assert groups[1]["categories"][0]["name"] == "Grouped" + + +def _content_nodes(md_text: str) -> list[SyntaxTreeNode]: + """Helper: parse markdown and return all block nodes.""" + md = MarkdownIt("commonmark") + root = SyntaxTreeNode(md.parse(md_text)) + return root.children + + +class TestParseSectionEntries: + def test_flat_entries(self): + nodes = _content_nodes( + "- [django](https://example.com/d) - A web framework.\n" + "- [flask](https://example.com/f) - A micro framework.\n" + ) + entries = _parse_section_entries(nodes) + assert len(entries) == 2 + assert entries[0]["name"] == "django" + assert entries[0]["url"] == "https://example.com/d" + assert "web framework" in entries[0]["description"] + assert entries[0]["also_see"] == [] + assert entries[1]["name"] == "flask" + + def test_link_only_entry(self): + nodes = _content_nodes("- [tool](https://x.com)\n") + entries = _parse_section_entries(nodes) + assert len(entries) == 1 + assert entries[0]["name"] == "tool" + assert entries[0]["description"] == "" + + def test_subcategorized_entries(self): + nodes = _content_nodes( + "- Algorithms\n" + " - [algos](https://x.com/a) - Algo lib.\n" + " - [sorts](https://x.com/s) - Sort lib.\n" + "- Design Patterns\n" + " - [patterns](https://x.com/p) - Pattern lib.\n" + ) + entries = _parse_section_entries(nodes) + assert len(entries) == 3 + assert entries[0]["name"] == "algos" + assert entries[2]["name"] == "patterns" + + def test_text_before_link_is_subcategory(self): + nodes = _content_nodes( + "- MySQL - [awesome-mysql](http://example.com/awesome-mysql/)\n" + " - [mysqlclient](https://example.com/mysqlclient) - MySQL connector.\n" + " - [pymysql](https://example.com/pymysql) - Pure Python MySQL driver.\n" + ) + entries = _parse_section_entries(nodes) + # awesome-mysql is a subcategory label, not an entry + assert len(entries) == 2 + names = [e["name"] for e in entries] + assert "awesome-mysql" not in names + assert "mysqlclient" in names + assert "pymysql" in names + + def test_also_see_sub_entries(self): + nodes = _content_nodes( + "- [asyncio](https://docs.python.org/3/library/asyncio.html) - Async I/O.\n" + " - [awesome-asyncio](https://github.com/timofurrer/awesome-asyncio)\n" + "- [trio](https://github.com/python-trio/trio) - Friendly async.\n" + ) + entries = _parse_section_entries(nodes) + assert len(entries) == 2 + assert entries[0]["name"] == "asyncio" + assert len(entries[0]["also_see"]) == 1 + assert entries[0]["also_see"][0]["name"] == "awesome-asyncio" + assert entries[1]["name"] == "trio" + assert entries[1]["also_see"] == [] + + def test_entry_count_includes_also_see(self): + readme = textwrap.dedent("""\ + # T + + --- + + ## Async + + - [asyncio](https://x.com) - Async I/O. + - [awesome-asyncio](https://y.com) + - [trio](https://z.com) - Friendly async. + + # Contributing + + Done. + """) + groups, _ = parse_readme(readme) + cats = groups[0]["categories"] + # 2 main entries + 1 also_see = 3 + assert cats[0]["entry_count"] == 3 + + def test_preview_first_four_names(self): + readme = textwrap.dedent("""\ + # T + + --- + + ## Libs + + - [alpha](https://x.com) - A. + - [beta](https://x.com) - B. + - [gamma](https://x.com) - C. + - [delta](https://x.com) - D. + - [epsilon](https://x.com) - E. + + # Contributing + + Done. + """) + groups, _ = parse_readme(readme) + cats = groups[0]["categories"] + assert cats[0]["preview"] == "alpha, beta, gamma, delta" + + def test_description_html_escapes_xss(self): + nodes = _content_nodes('- [lib](https://x.com) - A lib.\n') + entries = _parse_section_entries(nodes) + assert "\n") + html = _render_section_html(nodes) + assert "