diff --git a/DEBIAN/control b/DEBIAN/control new file mode 100644 index 0000000..a41e58a --- /dev/null +++ b/DEBIAN/control @@ -0,0 +1,12 @@ +Source: server22 +Maintainer: commrat +Section: python +Priority: optional +Build-Depends: python3.8, dh-python, python3-setuptools, python3-all, debhelper (>= 9) +Depends: python3, python-docopt +Standards-Version: 3.9.6 +Version: 13.2.2 +Package: server22 +Architecture: all +Description: Start a server, which listens on port 5000 + And listens. diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..cb55f71 --- /dev/null +++ b/Pipfile @@ -0,0 +1,12 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +docopt = "*" + +[dev-packages] + +[requires] +python_version = "3.8" diff --git a/README.md b/README.md new file mode 100644 index 0000000..40538c0 --- /dev/null +++ b/README.md @@ -0,0 +1,412 @@ +# Packaging guide + +## Usage +This is a minimalist project for making any python module/package installable on Debian based distributions. +The goal is: +1) install project locally +2) create .deb package +3) install .deb package +Once you cloned this repo to your local machine, you should run following commands to make it works: + +If install.sh isn't executable run this: +- sudo chmod +x install.sh + +install.sh provides all necessarily commands (read more bellow) for python installation on deb system & creates +server22 dir and .deb file in /tmp +- ./install.sh + +Installation folder and .deb package are moved to tmp dir, so we will continue from there +- cd /tmp + +That command will install package (version should change in the future, so use the right name of .deb package) +- sudo dpkg -i server22_13.2.2_all.deb + +Be aware, that server22 is using docopt and It has to be run with port argument +Now you can happily run your server :) +- server22 5000 + +If you (for some reason) want to uninstall this project just type this: +- sudo apt-get remove server22 -y + + +## SERVER22 info +Server22 is single module, which creates a socket and waits for the connection of client. + +## Trees: +- root dir where installation is applied: +├── DEBIAN +│   └── control +├── install.sh +├── Pipfile +├── Pipfile.lock +├── README.md +├── setup.py +└── src + └── server22 + ├── __init__.py + └── server22.py + +- dir created after installation: +├── DEBIAN +│   └── control +└── usr + ├── bin + │   └── server22 + ├── lib + │   └── python3 + │   └── dist-packages + │   ├── server22 + │   │   ├── __init__.py + │   │   └── server22.py + │   └── server22-2.2.2.egg-info + │   ├── dependency_links.txt + │   ├── entry_points.txt + │   ├── PKG-INFO + │   └── top_level.txt + └── share + └── doc + └── server22 + ├── copyright + └── changelog.gz + +## setup.py +setup.py in root folder is ESSENTIAL! In this project is used the bare minimum of it: +#!/usr/bin/python + +from setuptools import setup + +setup( + name='server22', + version='2.2.2', + description='Start a server, which listens on port 5000', + long_description='Server waits on client to join on localhost:5000.', + packages=['server22'], + package_dir={'':'src'}, + entry_points={"console_scripts": ["server22 = server22.server22:main"]}, +) + +You can find all possibilities and options of setup.py down below. + +## src/server22 +This source directory contains empty __init__.py file and server22.py +init file should be always here, beacuse python installation will looks for that and point this as source dir. +After that, server22 is run. + +## DEBIAN/control +In the past, that was essential to have a /debian directory and some files in it (including control). +Nowadays directory should be named /DEBIAN with capital letters and It is enough if it contains just control file. +dpkg will read the instructions and pack that to .deb +This is how It looks like: + +Source: server22 +Maintainer: commrat +Section: python +Priority: optional +Build-Depends: python3.8, dh-python, python3-setuptools, python3-all, debhelper (>= 9) +Depends: python3, python-docopt +Standards-Version: 3.9.6 +Version: 13.2.2 +Package: server22 +Architecture: all +Description: Start a server, which listens on port 5000 + And listens. + +NOTE: third party library docopt should be installed by pip to be functional in dpkg packaging. + +## Junk files in root dir +python installation will totally ignore your files in root folder (except source dir and setup.py), so feel free to +make some files You need (for example Pipfile, README, etc.) + +## install.sh +Shell script, which automatized the process of installation and packaging. +You can find more info about tasks performed in install.sh - comments. + +## python setup.py +For the long time, distutils was the most used library for python installations, however official Python Software Foundation +does not recommend this lib anymore. +setuptools were created and It offers more options, after making setuptools implemented in python installation It becomes standart. +Now we will take a look for some python setup.py use cases. + +* python setup.py build +Creates a build folder (which is necessary for installation) - but not actually install anything. We can think about that as some 'preparation for install'. +tree: +├── build +│   └── lib +│   └── server23 +│   ├── __init__.py +│   └── server23.py +├── DEBIAN +│   └── control +├── install.sh +├── setup.py +└── src + └── server23 + ├── __init__.py + └── server23.py + +* python setup.py install +Installation is 2 step action. First step = build. Second step = final local installation. +tree: +├── DEBIAN +│   └── control +├── build +│   └── lib +│   └── server23 +│   ├── __init__.py +│   └── server23.py +└── usr + ├── bin + │   └── server22 + ├── lib + │   └── python3 + │   └── dist-packages + │   ├── server22 + │   │   ├── __init__.py + │   │   └── server22.py + │   └── server22-2.2.2.egg-info + │   ├── dependency_links.txt + │   ├── entry_points.txt + │   ├── PKG-INFO + │   └── top_level.txt + └── share + └── doc + └── server22 + ├── copyright + └── changelog.gz + +* python setup.py sdist +It will create source distribution - and zip it, so you can ship it :) +tree: +├── DEBIAN +│   └── control +├── dist +│   └── server24-2.2.2.tar.gz +├── install.sh +├── m.md +├── setup.py +└── src + ├── server24 + │   ├── __init__.py + │   └── server24.py + └── server24.egg-info + ├── dependency_links.txt + ├── entry_points.txt + ├── PKG-INFO + ├── SOURCES.txt + └── top_level.txt + +* python setup.py develop +Executes build, installation but creates a simlink, so every changes made are immediately visible, without need of reinstall. + +* python setup.py bdist_rpm +Creates .rpm package, which can be installed. + +* python setup.py bdist_wininst +Creates an executable installer for Windows. + +NOTE: bdist_rpm & bdist_wininst does not work on Debian based distrubutions! You have to do that on suitable OS. + +## Structure of setup.py +- from PyPA (Python Packaging Authority), https://www.pypa.io/en/latest/ + +"""A setuptools based setup module. +See: +https://packaging.python.org/guides/distributing-packages-using-setuptools/ +https://github.com/pypa/sampleproject +""" + +- Always prefer setuptools over distutils +from setuptools import setup, find_packages +import pathlib + +here = pathlib.Path(__file__).parent.resolve() + +- Get the long description from the README file +long_description = (here / 'README.md').read_text(encoding='utf-8') + +- Arguments marked as "Required" below must be included for upload to PyPI. +- Fields marked as "Optional" may be commented out. + +setup( + # This is the name of your project. The first time you publish this + # package, this name will be registered for you. It will determine how + # users can install this project, e.g.: + # + # $ pip install sampleproject + # + # And where it will live on PyPI: https://pypi.org/project/sampleproject/ + # + # There are some restrictions on what makes a valid project name + # specification here: + # https://packaging.python.org/specifications/core-metadata/#name + name='sampleproject', # Required + + # Versions should comply with PEP 440: + # https://www.python.org/dev/peps/pep-0440/ + # + # For a discussion on single-sourcing the version across setup.py and the + # project code, see + # https://packaging.python.org/en/latest/single_source_version.html + version='2.0.0', # Required + + # This is a one-line description or tagline of what your project does. This + # corresponds to the "Summary" metadata field: + # https://packaging.python.org/specifications/core-metadata/#summary + description='A sample Python project', # Optional + + # This is an optional longer description of your project that represents + # the body of text which users will see when they visit PyPI. + # + # Often, this is the same as your README, so you can just read it in from + # that file directly (as we have already done above) + # + # This field corresponds to the "Description" metadata field: + # https://packaging.python.org/specifications/core-metadata/#description-optional + long_description=long_description, # Optional + + # Denotes that our long_description is in Markdown; valid values are + # text/plain, text/x-rst, and text/markdown + # + # Optional if long_description is written in reStructuredText (rst) but + # required for plain-text or Markdown; if unspecified, "applications should + # attempt to render [the long_description] as text/x-rst; charset=UTF-8 and + # fall back to text/plain if it is not valid rst" (see link below) + # + # This field corresponds to the "Description-Content-Type" metadata field: + # https://packaging.python.org/specifications/core-metadata/#description-content-type-optional + long_description_content_type='text/markdown', # Optional (see note above) + + # This should be a valid link to your project's main homepage. + # + # This field corresponds to the "Home-Page" metadata field: + # https://packaging.python.org/specifications/core-metadata/#home-page-optional + url='https://github.com/pypa/sampleproject', # Optional + + # This should be your name or the name of the organization which owns the + # project. + author='A. Random Developer', # Optional + + # This should be a valid email address corresponding to the author listed + # above. + author_email='author@example.com', # Optional + + # Classifiers help users find your project by categorizing it. + # + # For a list of valid classifiers, see https://pypi.org/classifiers/ + classifiers=[ # Optional + # How mature is this project? Common values are + # 3 - Alpha + # 4 - Beta + # 5 - Production/Stable + 'Development Status :: 3 - Alpha', + + # Indicate who your project is intended for + 'Intended Audience :: Developers', + 'Topic :: Software Development :: Build Tools', + + # Pick your license as you wish + 'License :: OSI Approved :: MIT License', + + # Specify the Python versions you support here. In particular, ensure + # that you indicate you support Python 3. These classifiers are *not* + # checked by 'pip install'. See instead 'python_requires' below. + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3 :: Only', + ], + + # This field adds keywords for your project which will appear on the + # project page. What does your project relate to? + # + # Note that this is a list of additional keywords, separated + # by commas, to be used to assist searching for the distribution in a + # larger catalog. + keywords='sample, setuptools, development', # Optional + + # When your source code is in a subdirectory under the project root, e.g. + # `src/`, it is necessary to specify the `package_dir` argument. + package_dir={'': 'src'}, # Optional + + # You can just specify package directories manually here if your project is + # simple. Or you can use find_packages(). + # + # Alternatively, if you just want to distribute a single Python file, use + # the `py_modules` argument instead as follows, which will expect a file + # called `my_module.py` to exist: + # + # py_modules=["my_module"], + # + packages=find_packages(where='src'), # Required + + # Specify which Python versions you support. In contrast to the + # 'Programming Language' classifiers above, 'pip install' will check this + # and refuse to install the project if the version does not match. See + # https://packaging.python.org/guides/distributing-packages-using-setuptools/#python-requires + python_requires='>=3.6, <4', + + # This field lists other packages that your project depends on to run. + # Any package you put here will be installed by pip when your project is + # installed, so they must be valid existing projects. + # + # For an analysis of "install_requires" vs pip's requirements files see: + # https://packaging.python.org/en/latest/requirements.html + install_requires=['peppercorn'], # Optional + + # List additional groups of dependencies here (e.g. development + # dependencies). Users will be able to install these using the "extras" + # syntax, for example: + # + # $ pip install sampleproject[dev] + # + # Similar to `install_requires` above, these must be valid existing + # projects. + extras_require={ # Optional + 'dev': ['check-manifest'], + 'test': ['coverage'], + }, + + # If there are data files included in your packages that need to be + # installed, specify them here. + package_data={ # Optional + 'sample': ['package_data.dat'], + }, + + # Although 'package_data' is the preferred approach, in some case you may + # need to place data files outside of your packages. See: + # http://docs.python.org/distutils/setupscript.html#installing-additional-files + # + # In this case, 'data_file' will be installed into '/my_data' + data_files=[('my_data', ['data/data_file'])], # Optional + + # To provide executable scripts, use entry points in preference to the + # "scripts" keyword. Entry points provide cross-platform support and allow + # `pip` to create the appropriate form of executable for the target + # platform. + # + # For example, the following would provide a command called `sample` which + # executes the function `main` from this package when invoked: + entry_points={ # Optional + 'console_scripts': [ + 'sample=sample:main', + ], + }, + + # List additional URLs that are relevant to your project as a dict. + # + # This field corresponds to the "Project-URL" metadata fields: + # https://packaging.python.org/specifications/core-metadata/#project-url-multiple-use + # + # Examples listed include a pattern for specifying where the package tracks + # issues, where the source is hosted, where to say thanks to the package + # maintainers, and where to support the project financially. The key is + # what's used to render the link text on PyPI. + project_urls={ # Optional + 'Bug Reports': 'https://github.com/pypa/sampleproject/issues', + 'Funding': 'https://donate.pypi.org', + 'Say Thanks!': 'http://saythanks.io/to/example', + 'Source': 'https://github.com/pypa/sampleproject/', + }, +) diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..0724456 --- /dev/null +++ b/install.sh @@ -0,0 +1,33 @@ +#!/bin/sh +set -e -x + +sudo rm -rf /tmp/server22 +python3 setup.py install --install-layout deb --root /tmp/server22 + +#read -p "Enter full path to your project folder: " PROJECT_PATH +#sudo rm -rf /home/commrat/Job/filesystem/server22/src/server22.egg-info +#sudo rm -rf /home/commrat/Job/filesystem/server22/build +# sudo rm -rf ${PROJECT_PATH}/build +# sudo rm -rf ${PROJECT_PATH}/src/server22.egg-info +sudo rm -rf ./src/server22.egg-info +sudo rm -rf ./build +tree +#sudo mkdir /tmp/server22/DEBIAN +sudo cp -R ./DEBIAN /tmp/server22 +#sudo cp -b /home/commrat/Job/filesystem/control /tmp/server22/DEBIAN +#sudo cp -b ../control /tmp/server22/DEBIAN + +sudo rm -r /tmp/server22/usr/lib/python3/dist-packages/server22/__pycache__ +tree /tmp/server22 +mkdir /tmp/server22/usr/share/ +mkdir /tmp/server22/usr/share/doc +mkdir /tmp/server22/usr/share/doc/server22/ +touch /tmp/server22/usr/share/doc/server22/copyright +touch /tmp/server22/usr/share/doc/server22/changelog.gz + +sudo chown -R root:staff /tmp/server22 +sudo chown -R root:root /tmp/server22/usr/share +tree /tmp/server22 +dpkg -b /tmp/server22 +lintian -c /tmp/server22.deb +dpkg-name -o /tmp/server22.deb diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..623af8c --- /dev/null +++ b/setup.py @@ -0,0 +1,19 @@ +#!/usr/bin/python + +from setuptools import setup + +setup( + name='server22', + version='2.2.2', + description='Start a server, which listens on port 5000', + long_description='Server waits on client to join on localhost:5000.', + #py_modules=['debcomm'], + packages=['server22'], + package_dir={'':'src'}, + entry_points={"console_scripts": ["server22 = server22.server22:main"]}, +) + +# FOR WHEEL INSTALATION (FOR UPLOAD TO PYPY) +# $ python setup.py bdist_wheel +# LOCAL INSTALLATION (-e for continual developement, it's just link) +# $ pip install -e . diff --git a/src/server22/__init__.py b/src/server22/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/server22/server22.py b/src/server22/server22.py new file mode 100644 index 0000000..0934168 --- /dev/null +++ b/src/server22/server22.py @@ -0,0 +1,69 @@ +#!/usr/bin/python3 +''' +server. + +Usage: + server.py [options] + +Arguments: + Specify the port + +Options: + --sleep Specify how often data will be send (secs). +''' + +import socket +import time +import random +import sys +import string +import docopt + + +def string_generator(string_len): + chars = list(string.ascii_lowercase) + list(string.ascii_uppercase) + list(string.digits) + list_of_chars = [random.choice(chars) for _ in range(string_len)] + final_string = ''.join(list_of_chars) + return final_string + + +def send_data(c, sleep_time): + while 1: + string_len = random.randint(1, 120) + generated_string = string_generator(string_len) + try: + c.send(generated_string.encode()) + except BrokenPipeError: + print('Connection failed.') + return 0 + time.sleep(sleep_time) + + +def main(): + args = docopt.docopt(__doc__) + port_arg = args[''] + sleep_time = args['--sleep'] + sleep_time = float(sleep_time) if sleep_time else 1 + port = int(port_arg) + + s = socket.socket() + s.bind(('', port)) + s.listen() + print('Socket is listening.') + while 1: + try: + print('Waiting for connection.') + c, addr = s.accept() + if c: + print('Got connection from: ', addr) + print('Socket data: ', c) + send_data(c, sleep_time) + except KeyboardInterrupt: + print('Program ends by user request.') + break + s.close() + return 0 + + +if __name__ == '__main__': + sys.exit(main())