Tools: Guide to publish your first PyPI library ๐Ÿš€

Tools: Guide to publish your first PyPI library ๐Ÿš€

Why to create a py package? ๐Ÿค”๐Ÿ ## ๐Ÿ“‚ Folder structure needed (yes, structure matters) ## Content of pyproject.toml โค๏ธโ€๐Ÿ”ฅ ## Contents of MANIFEST.in (donโ€™t skip ๐Ÿ™) ## Key rules ๐Ÿšจ ## Requirements ๐Ÿ“‹ ## Steps to create & upload ๐Ÿš€ ## Install build tools ๐Ÿ› ๏ธ ## Build your package ๐Ÿ“ฆ ## Upload to testPyPI (optional but smart ๐Ÿง ) ## Upload to real PyPI ๐ŸŒ ## Something more ๐Ÿ‘€ ## Common mistakes โŒ ## Pro tips ๐Ÿ’ก ๐Ÿ‘€ Do you also want to be included in that elite, mysterious, and totally brag-worthy list of contributors for a public PyPI package? Yes? No? Still scrolling like itโ€™s an Instagram reel? ๐Ÿ“ฑ Cool, letโ€™s fix that. Before you start dreaming about PyPI fame (and GitHub stars ๐ŸŒŸ that your relatives wonโ€™t understand), pause for a reality check. First, decide if your project is actually PyPI-worthy. Before touching configs, sanity-check that your project is reusable (no hardcoded paths, credentials, or โ€œworks only on my laptopโ€ local files ๐Ÿ’ปโŒ) & has a clear entry point (functions/CLI). If your script is โ€œrun once and forgetโ€ โ€” PyPI is overkill ๐Ÿ’€ If others (or future-you, after 3 months of memory loss ๐Ÿง ) can reuse it โ†’ publish it ๐Ÿš€ Think of it like the World Cup ๐Ÿ โ€” not every gully cricketer makes it to the squad, but the good ones do ๐Ÿ˜‰ Same rules apply here. Just like Indiaโ€™s budget announcements ๐Ÿ“Š โ€” boring, structured, but very important. Mess this up, and PyPI will treat your package the way Twitter treats breaking news โ€” harshly ๐Ÿ˜ฌ๐Ÿฆ. This is the heart of deployment. Mess this up and your package dies faster than a government promise during elections ๐Ÿ—ณ๏ธ๐Ÿ˜Œ. Yes, every line matters. No, PyPI doesnโ€™t care if โ€œit worked locallyโ€ ๐Ÿ˜ŒโŒ. Ensures non-code files are packaged. Skipping this is like hosting the World Cup final without a trophy ๐Ÿ† โ€” technically possible, emotionally devastating. Without this, your PyPI page may break. And users will judge you silently ๐Ÿค. There are some key rules while uploading your package on the PyPI website. These are non-negotiable โ€” like ICC rules during finals ๐Ÿ˜ค๐Ÿ: ; PyPI account can be created from here & tutorial for the same is in this youtube video TestPyPI account can be created from here & tutorial for the same is in this youtube video Yes, two accounts. No, PyPI doesnโ€™t trust you immediately โ€” neither should it ๐Ÿ˜Œ๐Ÿ”. If this executes successfully (pray to the Python gods ๐Ÿ๐Ÿ™), it will create: If it fails โ€” congrats ๐ŸŽ‰, youโ€™re officially learning ๐Ÿคจ Then install your uploaded package from testPyPI: Test import + functionality here. Break things now, not in production ๐Ÿ˜ฌ๐Ÿ”ฅ. ; make sure dist/* uploads everything inside dist/. And boom ๐Ÿ’ฅ โ€” Congratulations, youโ€™ve just uploaded your own package! ๐ŸŽ‰๐ŸŽ‰ Take a moment. Screenshot it. Flex on LinkedIn ๐Ÿ˜Ž. โŒ Hardcoded paths โŒ Forgot README / LICENSE โŒ Version conflict โŒ Import errors due to bad structure โŒ Uploading secrets accidentally (this one hurts the most ๐Ÿ’€๐Ÿ”) โœ… Add CLI using entry-points โœ… Automate release via GitHub Actions โœ… Tag versions (v0.1.0) โœ… Add __version__ inside package โœ… Use ruff / black before publishing Clean code = happy users ๐Ÿ˜Š. Hope youโ€™ve successfully created your first package on PyPI ๐Ÿ๐ŸŽฏ Do share your package name in comments โ€” letโ€™s hype each other like World Cup fans ๐Ÿ‡ฎ๐Ÿ‡ณ๐Ÿ. Iโ€™ve recently committed pyshrink. Do check it out ๐Ÿ‘€โœจ. Also, if youโ€™ve got questions, confusion, or existential doubts about packaging โ€” comment it out ๐Ÿ’ฌ๐Ÿ˜„. Author ๐Ÿ˜Ž Nitin Kumar Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to ? It will become hidden in your post, but will still be visible via the comment's permalink. as well , this person and/or CODE_BLOCK: my_package/ โ”‚ โ”œโ”€โ”€ src/ โ”‚ โ””โ”€โ”€ my_package/ โ”‚ โ”œโ”€โ”€ __init__.py โ”‚ โ”œโ”€โ”€ core.py โ”‚ โ””โ”€โ”€ utils.py โ”‚ โ”œโ”€โ”€ tests/ โ”‚ โ””โ”€โ”€ test_basic.py โ”‚ โ”œโ”€โ”€ README.md โ”œโ”€โ”€ LICENSE โ”œโ”€โ”€ pyproject.toml โ”œโ”€โ”€ MANIFEST.in โ””โ”€โ”€ .gitignore CODE_BLOCK: my_package/ โ”‚ โ”œโ”€โ”€ src/ โ”‚ โ””โ”€โ”€ my_package/ โ”‚ โ”œโ”€โ”€ __init__.py โ”‚ โ”œโ”€โ”€ core.py โ”‚ โ””โ”€โ”€ utils.py โ”‚ โ”œโ”€โ”€ tests/ โ”‚ โ””โ”€โ”€ test_basic.py โ”‚ โ”œโ”€โ”€ README.md โ”œโ”€โ”€ LICENSE โ”œโ”€โ”€ pyproject.toml โ”œโ”€โ”€ MANIFEST.in โ””โ”€โ”€ .gitignore CODE_BLOCK: my_package/ โ”‚ โ”œโ”€โ”€ src/ โ”‚ โ””โ”€โ”€ my_package/ โ”‚ โ”œโ”€โ”€ __init__.py โ”‚ โ”œโ”€โ”€ core.py โ”‚ โ””โ”€โ”€ utils.py โ”‚ โ”œโ”€โ”€ tests/ โ”‚ โ””โ”€โ”€ test_basic.py โ”‚ โ”œโ”€โ”€ README.md โ”œโ”€โ”€ LICENSE โ”œโ”€โ”€ pyproject.toml โ”œโ”€โ”€ MANIFEST.in โ””โ”€โ”€ .gitignore CODE_BLOCK: [build-system] requires = ["setuptools>=61.0", "wheel"] build-backend = "setuptools.build_meta" [project] name = "my-package" version = "0.1.0" description = "Utility to validate API responses using schema rules" readme = "README.md" license = { file = "LICENSE" } authors = [ { name = "FIRSTNAME LASTNAME", email = "[email protected]" } ] requires-python = ">=3.8" dependencies = [ ] [project.urls] Homepage = "https://github.com/yourname/my-package" CODE_BLOCK: [build-system] requires = ["setuptools>=61.0", "wheel"] build-backend = "setuptools.build_meta" [project] name = "my-package" version = "0.1.0" description = "Utility to validate API responses using schema rules" readme = "README.md" license = { file = "LICENSE" } authors = [ { name = "FIRSTNAME LASTNAME", email = "[email protected]" } ] requires-python = ">=3.8" dependencies = [ ] [project.urls] Homepage = "https://github.com/yourname/my-package" CODE_BLOCK: [build-system] requires = ["setuptools>=61.0", "wheel"] build-backend = "setuptools.build_meta" [project] name = "my-package" version = "0.1.0" description = "Utility to validate API responses using schema rules" readme = "README.md" license = { file = "LICENSE" } authors = [ { name = "FIRSTNAME LASTNAME", email = "[email protected]" } ] requires-python = ">=3.8" dependencies = [ ] [project.urls] Homepage = "https://github.com/yourname/my-package" CODE_BLOCK: include README.md include LICENSE CODE_BLOCK: include README.md include LICENSE CODE_BLOCK: include README.md include LICENSE COMMAND_BLOCK: pip install --upgrade build twine COMMAND_BLOCK: pip install --upgrade build twine COMMAND_BLOCK: pip install --upgrade build twine CODE_BLOCK: python -m build CODE_BLOCK: python -m build CODE_BLOCK: python -m build CODE_BLOCK: dist/ โ”œโ”€โ”€ my_package-0.1.0-py3-none-any.whl โ””โ”€โ”€ my_package-0.1.0.tar.gz CODE_BLOCK: dist/ โ”œโ”€โ”€ my_package-0.1.0-py3-none-any.whl โ””โ”€โ”€ my_package-0.1.0.tar.gz CODE_BLOCK: dist/ โ”œโ”€โ”€ my_package-0.1.0-py3-none-any.whl โ””โ”€โ”€ my_package-0.1.0.tar.gz CODE_BLOCK: twine upload --repository testpypi dist/* CODE_BLOCK: twine upload --repository testpypi dist/* CODE_BLOCK: twine upload --repository testpypi dist/* COMMAND_BLOCK: pip install --index-url https://test.pypi.org/simple/ my-package COMMAND_BLOCK: pip install --index-url https://test.pypi.org/simple/ my-package COMMAND_BLOCK: pip install --index-url https://test.pypi.org/simple/ my-package CODE_BLOCK: twine upload dist/* CODE_BLOCK: twine upload dist/* CODE_BLOCK: twine upload dist/* - Name must be unique on PyPI (yes, utils, helpers, test-lib are already taken ๐Ÿ˜‘) - Version must follow semver - Once uploaded โ†’ cannot overwrite the same version (PyPI never forgets ๐Ÿ˜ˆ) - A sample project which should adhere to these conditions - Any code editor. Iโ€™m using VS Code here (because obviously ๐Ÿ’™) - A valid package name should be unique โš ๏ธ - A PyPI & test.pypi Account - build โ†’ creates wheel & source - twine โ†’ uploads to PyPI (your final boss ๐ŸŽฎ)