From 3211724943507438d8c1120d2b5e414645c51525 Mon Sep 17 00:00:00 2001 From: Muhammad Fiaz Date: Wed, 20 Nov 2024 20:13:14 +0530 Subject: [PATCH] added help docs (#3) --- setup.py | 55 ++--------- setups/cli.py | 257 ++++++++++++++++++++++++++----------------------- setups/help.py | 124 ++++++++++++++++++++++++ 3 files changed, 268 insertions(+), 168 deletions(-) create mode 100644 setups/help.py diff --git a/setup.py b/setup.py index 44d658a..820f8a4 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,7 @@ from setuptools import setup, find_packages +from setups.help import usage_instructions + VERSION = "0.0.2" # Version of your package DESCRIPTION = 'Setups: Dynamically generate setup.py for Python projects.' @@ -35,7 +37,9 @@ 'wheel', # Add wheel to create binary distributions ], setup_requires=['pytest-runner'], # For running tests during installation - tests_require=['pytest'], # Dependencies for running tests + extras_require={ # Replacing 'tests_require' with 'extras_require' + 'tests': ['pytest'], # Optional testing dependencies + }, license='MIT', # License for the project project_urls={ # Additional URLs related to your project 'Source Code': 'https://github.com/muhammad-fiaz/setups-python', @@ -50,56 +54,15 @@ ) # Guide for the user after installation -print(""" +print(r""" ************************************************** Installation Complete! -Once you've installed the package, you can now use the 'setup' command to generate setup.py for your Python project. - -Usage: - setup - -This will ask you a series of questions to generate a setup.py file for your project. Once the setup.py is generated: - -๐ŸŽ‰ Here's what you need to do next to upload your package to PyPI: - -Step 1: Create the Distribution --------------------------------- -Run the following commands to build the distribution: - - python setup.py sdist bdist_wheel - -This creates both source (.tar.gz) and wheel (.whl) distributions in the dist/ folder. - -Step 2: Upload to PyPI ------------------------ -Once you've built the distribution, upload it to PyPI with the following: - - twine upload dist/* - -This will prompt you for your PyPI credentials and upload your package. - -You're all set to share your package with the world! ๐Ÿš€ - - ------------------- - | _______________ | - | |XXXXXXXXXXXXX| | - | |XXXXXXXXXXXXX| | - | |XXXXXXXXXXXXX| | - | |XXXXXXXXXXXXX| | - | |XXXXXXXXXXXXX| | - |_________________| - _[_______]_ - ___[___________]___ -| [_____] []|__ -| [_____] []| \__ -L___________________J \ \___\/ - ___________________ /\\ -/###################\\ (__) - Thank you for using our tool. For more details on usage, please refer to the documentation: https://github.com/muhammad-fiaz/setups-python#usage +""") +usage_instructions() +print(r""" ************************************************** """) - diff --git a/setups/cli.py b/setups/cli.py index 45b0081..8a0b58b 100644 --- a/setups/cli.py +++ b/setups/cli.py @@ -1,122 +1,135 @@ -import click - -# Define a comprehensive list of valid licenses from GitHub -VALID_LICENSES = [ - 'MIT', 'Apache-2.0', 'GPL-3.0', 'LGPL-3.0', 'BSD-2-Clause', 'BSD-3-Clause', - 'CC0-1.0', 'MPL-2.0', 'EPL-2.0', 'AGPL-3.0', 'MIT-0', 'ISC', 'Unlicense' -] - -# Define available classifiers for easier reference -DEFAULT_CLASSIFIERS = [ - "Development Status :: 5 - Production/Stable", - "Intended Audience :: Developers", - "Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT", - "Operating System :: OS Independent" -] - -@click.command() -@click.argument("project_name") -def generate_setup(project_name): - """ - Generate a complete setup.py and README.md file for a new Python project, asking the user for all details dynamically. - """ - click.echo("Generating setup.py and README.md...") - - # Asking for required details - version = click.prompt("Version (e.g., 0.1.0)", type=str, default="0.1.0") - description = click.prompt("Short project description (optional)", type=str, default="") - long_description = click.prompt("Long description (optional, use content from your README.md)", type=str, default="") - - # Author info now optional - author = click.prompt("Author name (optional)", type=str, default="") - author_email = click.prompt("Author email (optional)", type=str, default="") - - # Asking for license selection by index - click.echo("Select a license:") - for idx, license in enumerate(VALID_LICENSES): - click.echo(f"{idx}. {license}") - license_idx = click.prompt("License (Enter the index number)", type=int, default=0) - license_type = VALID_LICENSES[license_idx] - - python_version = click.prompt("Minimum Python version required (e.g., 3.8)", type=str, default="3.8") - - # Optional fields with defaults if left empty - dependencies = click.prompt("Comma-separated list of dependencies (leave empty for none)", default="", type=str) - dependencies = [dep.strip() for dep in dependencies.split(",") if dep.strip()] - - test_dependencies = click.prompt("Comma-separated list of test dependencies (leave empty for none)", default="", - type=str) - test_dependencies = [dep.strip() for dep in test_dependencies.split(",") if dep.strip()] - - # URLs now optional - project_url = click.prompt("Project URL (optional)", type=str, default="") - bug_tracker_url = click.prompt("Bug tracker URL (optional)", type=str, default="") - documentation_url = click.prompt("Documentation URL (optional)", type=str, default="") - - # Ask if the user wants to specify classifiers - click.echo("Would you like to specify 'Development Status', 'Intended Audience', and 'Programming Language'?") - use_classifiers = click.confirm("Specify classifiers?", default=False) - - classifiers = DEFAULT_CLASSIFIERS - if use_classifiers: - # Development Status - development_status = click.prompt("Select 'Development Status' (e.g., 1 - Planning, 2 - Pre-Alpha, etc.)", type=str, default="5 - Production/Stable") - classifiers[0] = f"Development Status :: {development_status}" - - # Intended Audience - audience = click.prompt("Select 'Intended Audience' (e.g., Developers, Education, etc.)", type=str, default="Developers") - classifiers[1] = f"Intended Audience :: {audience}" - - # Programming Language - language = click.prompt("Select 'Programming Language' (e.g., Python :: 3)", type=str, default="Python :: 3") - classifiers[2] = f"Programming Language :: {language}" - - # Prepare the content for the README.md file - readme_content = f"# {project_name}\n\n{long_description if long_description else 'Project description'}\n" - - # Prepare the content for the setup.py file - setup_content = f""" -from setuptools import setup, find_packages - -VERSION = "{version}" # Version of your package -DESCRIPTION = '{description if description else "Project description"}' # Short description - -# Long description of the project (can be pulled from README.md) -LONG_DESCRIPTION = '''{long_description if long_description else 'Detailed project description from README.md'}''' - -setup( - name="{project_name}", # Name of your package - version=VERSION, # Package version - author="{author if author else ''}", # Author name - author_email="{author_email if author_email else ''}", # Author's email - description=DESCRIPTION, # Short description - long_description=LONG_DESCRIPTION, # Detailed description from README.md - long_description_content_type="text/markdown", # Format of the long description - url="{project_url if project_url else ''}", # URL to the project's GitHub page - packages=find_packages(), # Automatically find all packages in the directory - classifiers={classifiers}, # List of classifiers to categorize your package - python_requires=">={python_version}", # Minimum Python version required - install_requires={dependencies}, # List of dependencies - setup_requires=["pytest-runner"], # For running tests during installation - tests_require={test_dependencies}, # Specify dependencies needed for running tests - license="{license_type}", # License under which the project is released - project_urls={{ # Additional URLs related to your project - "Source Code": "{project_url}" , - "Bug Tracker": "{bug_tracker_url}", - "Documentation": "{documentation_url}", - }}, -) -""" - - # Create the README.md and setup.py files in the current directory - with open("README.md", "w") as readme_file: - readme_file.write(readme_content) - - with open("setup.py", "w") as setup_file: - setup_file.write(setup_content) - - print(f"README.md and setup.py have been successfully generated for project '{project_name}'.") - -if __name__ == "__main__": - generate_setup() +import sys + +import click + +from setups.help import print_help + +# Define a comprehensive list of valid licenses from GitHub +VALID_LICENSES = [ + 'MIT', 'Apache-2.0', 'GPL-3.0', 'LGPL-3.0', 'BSD-2-Clause', 'BSD-3-Clause', + 'CC0-1.0', 'MPL-2.0', 'EPL-2.0', 'AGPL-3.0', 'MIT-0', 'ISC', 'Unlicense' +] + +# Define available classifiers for easier reference +DEFAULT_CLASSIFIERS = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT", + "Operating System :: OS Independent" +] + +@click.command(help="Generate a complete setup.py and README.md file for a new Python project.") +@click.argument("project_name", required=False) +@click.option('--help', '-h', is_flag=True, help="Show this message and exit.") +def generate_setup(project_name, help): + if help: + print_help() + else: + if not project_name: + click.echo("Error: Missing argument 'PROJECT_NAME'.") + click.echo(generate_setup.get_help(click.Context(generate_setup))) + return + + """ + Generate a complete setup.py and README.md file for a new Python project, asking the user for all details dynamically. + """ + click.echo("Generating setup.py and README.md...") + + # Asking for required details + version = click.prompt("Version (e.g., 0.1.0)", type=str, default="0.1.0") + description = click.prompt("Short project description (optional)", type=str, default="") + long_description = click.prompt("Long description (optional, use content from your README.md)", type=str, default="") + + # Author info now optional + author = click.prompt("Author name (optional)", type=str, default="") + author_email = click.prompt("Author email (optional)", type=str, default="") + + # Asking for license selection by index + click.echo("Select a license:") + for idx, license in enumerate(VALID_LICENSES): + click.echo(f"{idx}. {license}") + license_idx = click.prompt("License (Enter the index number)", type=int, default=0) + license_type = VALID_LICENSES[license_idx] + + python_version = click.prompt("Minimum Python version required (e.g., 3.8)", type=str, default="3.8") + + # Optional fields with defaults if left empty + dependencies = click.prompt("Comma-separated list of dependencies (leave empty for none)", default="", type=str) + dependencies = [dep.strip() for dep in dependencies.split(",") if dep.strip()] + + test_dependencies = click.prompt("Comma-separated list of test dependencies (leave empty for none)", default="", + type=str) + test_dependencies = [dep.strip() for dep in test_dependencies.split(",") if dep.strip()] + + # URLs now optional + project_url = click.prompt("Project URL (optional)", type=str, default="") + bug_tracker_url = click.prompt("Bug tracker URL (optional)", type=str, default="") + documentation_url = click.prompt("Documentation URL (optional)", type=str, default="") + + # Ask if the user wants to specify classifiers + click.echo("Would you like to specify 'Development Status', 'Intended Audience', and 'Programming Language'?") + use_classifiers = click.confirm("Specify classifiers?", default=False) + + classifiers = DEFAULT_CLASSIFIERS + if use_classifiers: + # Development Status + development_status = click.prompt("Select 'Development Status' (e.g., 1 - Planning, 2 - Pre-Alpha, etc.)", type=str, default="5 - Production/Stable") + classifiers[0] = f"Development Status :: {development_status}" + + # Intended Audience + audience = click.prompt("Select 'Intended Audience' (e.g., Developers, Education, etc.)", type=str, default="Developers") + classifiers[1] = f"Intended Audience :: {audience}" + + # Programming Language + language = click.prompt("Select 'Programming Language' (e.g., Python :: 3)", type=str, default="Python :: 3") + classifiers[2] = f"Programming Language :: {language}" + + # Prepare the content for the README.md file + readme_content = f"# {project_name}\n\n{long_description if long_description else 'Project description'}\n" + + # Prepare the content for the setup.py file + setup_content = f""" + from setuptools import setup, find_packages + + VERSION = "{version}" # Version of your package + DESCRIPTION = '{description if description else "Project description"}' # Short description + + # Long description of the project (can be pulled from README.md) + LONG_DESCRIPTION = '''{long_description if long_description else 'Detailed project description from README.md'}''' + + setup( + name="{project_name}", # Name of your package + version=VERSION, # Package version + author="{author if author else ''}", # Author name + author_email="{author_email if author_email else ''}", # Author's email + description=DESCRIPTION, # Short description + long_description=LONG_DESCRIPTION, # Detailed description from README.md + long_description_content_type="text/markdown", # Format of the long description + url="{project_url if project_url else ''}", # URL to the project's GitHub page + packages=find_packages(), # Automatically find all packages in the directory + classifiers={classifiers}, # List of classifiers to categorize your package + python_requires=">={python_version}", # Minimum Python version required + install_requires={dependencies}, # List of dependencies + setup_requires=["pytest-runner"], # For running tests during installation + extras_require={{'test': {test_dependencies}}}, + license="{license_type}", # License under which the project is released + project_urls={{ # Additional URLs related to your project + "Source Code": "{project_url}" + "Bug Tracker": "{bug_tracker_url}" + "Documentation": "{documentation_url}" + }}, + ) + """ + + # Create the README.md and setup.py files in the current directory + with open("README.md", "w") as readme_file: + readme_file.write(readme_content) + + with open("setup.py", "w") as setup_file: + setup_file.write(setup_content) + + print(f"README.md and setup.py have been successfully generated for project '{project_name}'.") + +if __name__ == "__main__": + generate_setup() \ No newline at end of file diff --git a/setups/help.py b/setups/help.py new file mode 100644 index 0000000..8e71670 --- /dev/null +++ b/setups/help.py @@ -0,0 +1,124 @@ + + +def usage_instructions(): + print(r""" +Quick Start Guide: +------------------ +To create a `setup.py` file for your project, use: + + setup + +For help, use: + + setup --help or setup -h + +This will provide details on usage and options for generating the `setup.py` file. +""") + + +def build_and_publish_steps(): + print(r""" +๐Ÿš€ Steps to Build and Publish Your Package on PyPI ๐Ÿš€ + +Step 1: Build Your Distribution +------------------------------ +Use the following command to build both source (`.tar.gz`) and wheel (`.whl`) distributions: + + python setup.py sdist bdist_wheel + or + python -m build + +This will generate your package files in the `dist/` directory. + +Step 2: Upload to PyPI +---------------------- +Upload your package using `twine`: + + twine upload dist/* + +Youโ€™ll be prompted for your PyPI credentials. Once uploaded, your package will be live for everyone to use! +""") + + +def testing_and_validation(): + print(r""" +๐Ÿงช Testing and Validation ๐Ÿงช + +1. **Install the Package Locally** + Test your package installation: + + pip install . + +2. **Editable Installation for Development** + Use this to test changes to your package without reinstalling: + + pip install -e . + +3. **Run Tests** + Use `pytest` for testing your code: + + pytest + +4. **Check Package Metadata** + Validate the correctness of your metadata using `twine`: + + twine check dist/* + +5. **Clean Build Artifacts** + Remove previously generated build files: + + python setup.py clean --all +""") + + +def additional_build_commands(): + print(r""" +๐Ÿ“ฆ Additional Build Commands ๐Ÿ“ฆ + +1. **Generate Only Source Distribution**: + python setup.py sdist + +2. **Generate Only Wheel Distribution**: + python setup.py bdist_wheel + +3. **Use `python -m build` (Recommended)**: + python -m build + + This method automatically handles both source and wheel distributions efficiently. +""") + + +def print_footer(): + print(r""" +Helpful Resources: +------------------- +๐Ÿ‘‰ [GitHub: setups-python Documentation](https://github.com/muhammad-fiaz/setups-python#usage) + + ------------------- + | _______________ | + | |XXXXXXXXXXXXX| | + | |XXXXXXXXXXXXX| | + | |XXXXXXXXXXXXX| | + | |XXXXXXXXXXXXX| | + | |XXXXXXXXXXXXX| | + |_________________| + _[_______]_ + ___[___________]___ +| [_____] []|__ +| [_____] []| \__ +L___________________J \ \___\/ + ___________________ /\\ +/###################\\ (__) + +Thank you for choosing our tool! ๐Ÿ˜Š +************************************************** +""") + + +def print_help(): + usage_instructions() + build_and_publish_steps() + testing_and_validation() + additional_build_commands() + print_footer() +