From 898c4786010ef1dc970a16fbe1b8e166c39b9213 Mon Sep 17 00:00:00 2001 From: "David L. Day" <1132144+davidlday@users.noreply.github.com> Date: Sat, 28 Aug 2021 09:25:34 -0400 Subject: [PATCH] Maintenance updated 2021 08 (#27) * standard workflows * moved code to src * added markdownlint config * converted readme to markdown * formatted with black * updated build files * removed 2.x from testing * added install_requires * v1.3.2 --- .github/workflows/pypi-publish.yml | 21 ++- .github/workflows/pypi-test-publish.yml | 31 ++++ .github/workflows/python-ci.yml | 48 +++++-- .github/workflows/python-lint.yml | 48 ------- .markdownlint.yaml | 2 + MANIFEST.in | 4 +- README.md | 129 +++++++++++++++++ README.rst | 135 ------------------ prosegrinder/VERSION | 1 - pyproject.toml | 6 + setup.cfg | 48 ++++++- setup.py | 53 +------ src/prosegrinder/VERSION | 1 + .../prosegrinder}/__init__.py | 0 .../prosegrinder}/__main__.py | 0 .../prosegrinder}/dictionary.py | 0 .../prosegrinder}/fragment.py | 0 .../prosegrinder}/fragment_container.py | 0 .../prosegrinder}/paragraph.py | 0 {prosegrinder => src/prosegrinder}/prose.py | 0 .../prosegrinder}/readability_scores.py | 0 .../prosegrinder}/sentence.py | 0 {prosegrinder => src/prosegrinder}/word.py | 0 tests/test_cli.py | 4 +- 24 files changed, 271 insertions(+), 260 deletions(-) create mode 100644 .github/workflows/pypi-test-publish.yml delete mode 100644 .github/workflows/python-lint.yml create mode 100644 .markdownlint.yaml create mode 100644 README.md delete mode 100644 README.rst delete mode 100644 prosegrinder/VERSION create mode 100644 pyproject.toml create mode 100644 src/prosegrinder/VERSION rename {prosegrinder => src/prosegrinder}/__init__.py (100%) rename {prosegrinder => src/prosegrinder}/__main__.py (100%) rename {prosegrinder => src/prosegrinder}/dictionary.py (100%) rename {prosegrinder => src/prosegrinder}/fragment.py (100%) rename {prosegrinder => src/prosegrinder}/fragment_container.py (100%) rename {prosegrinder => src/prosegrinder}/paragraph.py (100%) rename {prosegrinder => src/prosegrinder}/prose.py (100%) rename {prosegrinder => src/prosegrinder}/readability_scores.py (100%) rename {prosegrinder => src/prosegrinder}/sentence.py (100%) rename {prosegrinder => src/prosegrinder}/word.py (100%) diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml index f657b6c..f92f21e 100644 --- a/.github/workflows/pypi-publish.yml +++ b/.github/workflows/pypi-publish.yml @@ -1,6 +1,3 @@ -# This workflows will upload a Python Package using Twine when a release is created -# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries - name: Upload Python Package on: @@ -10,9 +7,10 @@ on: jobs: deploy: runs-on: ubuntu-latest - steps: - uses: actions/checkout@v2 + with: + submodules: true - name: Set up Python uses: actions/setup-python@v2 with: @@ -20,11 +18,12 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install setuptools wheel twine - - name: Build and publish - env: - TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} - TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} + pip install setuptools wheel twine build + - name: Build Dists run: | - python setup.py sdist bdist_wheel - twine upload dist/* + python -m build + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + user: __token__ + password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/pypi-test-publish.yml b/.github/workflows/pypi-test-publish.yml new file mode 100644 index 0000000..e47797c --- /dev/null +++ b/.github/workflows/pypi-test-publish.yml @@ -0,0 +1,31 @@ +name: Test Upload Python Package + +on: + push: + tags: + - "v*" + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + submodules: true + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: "3.x" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install setuptools wheel twine build + - name: Build Dists + run: | + python -m build + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + user: __token__ + password: ${{ secrets.TEST_PYPI_API_TOKEN }} + repository_url: https://test.pypi.org/legacy/ diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml index e97cb1f..f9971da 100644 --- a/.github/workflows/python-ci.yml +++ b/.github/workflows/python-ci.yml @@ -1,30 +1,62 @@ -# This workflow will install Python dependencies, run tests and lint with a variety of Python versions -# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions - name: Python CI on: [push, pull_request] jobs: - build: + lint: + runs-on: ubuntu-latest + steps: + - name: Check Out + uses: actions/checkout@v2 + with: + submodules: true + - name: Black + uses: psf/black@stable + - name: Pylint + uses: cclauss/GitHub-Action-for-pylint@0.7.0 + with: + args: pip install .; pylint src/**/*.py + + test: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.6, 3.7, 3.8, 3.8, 3.9] - + python-version: [3.6, 3.7, 3.8, 3.9] steps: - uses: actions/checkout@v2 + with: + submodules: true - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | - pip install --upgrade pip + python -m pip install --upgrade pip - name: Install module run: | - python ./setup.py develop + pip install -e . - name: Test with pytest run: | pip install pytest pytest + + check_build: + runs-on: ubuntu-latest + steps: + - name: Check Out + uses: actions/checkout@v2 + with: + submodules: true + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: "3.x" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install setuptools wheel twine build + - name: Build + run: python -m build + - name: Check + run: twine check dist/* diff --git a/.github/workflows/python-lint.yml b/.github/workflows/python-lint.yml deleted file mode 100644 index ec49772..0000000 --- a/.github/workflows/python-lint.yml +++ /dev/null @@ -1,48 +0,0 @@ -# This workflow will install Python dependencies, run tests and lint with a variety of Python versions -# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions - -name: Python Lint - -on: [pull_request] - -jobs: - lint: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - # - name: Set up Python 3.x - # uses: actions/setup-python@v2 - # with: - # python-version: "3.x" - # - name: Install dependencies - # run: | - # pip install --upgrade pip - # pip install pylint black - # pip install . - - name: Python Linting - uses: ricardochaves/python-lint@v1.4.0 - with: - python-root-list: "prosegrinder setup.py" - use-pylint: true - use-pycodestyle: false - use-flake8: false - use-black: true - use-mypy: false - use-isort: true - extra-pylint-options: "" - extra-pycodestyle-options: "" - extra-flake8-options: "" - extra-black-options: "" - extra-mypy-options: "" - extra-isort-options: "" - # - name: Install build dependencies - # run: | - # pip install --upgrade pip setuptools wheel - # - name: Install module & dependencies - # run: | - # python ./setup.py install - # - name: Lint with Prospector - # uses: jpetrucciani/prospector-check@1.3.1 - # with: - # path: . diff --git a/.markdownlint.yaml b/.markdownlint.yaml new file mode 100644 index 0000000..ad9cabf --- /dev/null +++ b/.markdownlint.yaml @@ -0,0 +1,2 @@ +MD034: false # no-bare-urls: Bare URL used +MD024: false # no-duplicate-heading/no-duplicate-header: Multiple headings with the same content diff --git a/MANIFEST.in b/MANIFEST.in index 5539c7e..42f7cc6 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,7 +1,7 @@ include MANIFEST.in -include README.rst +include README.md include LICENSE -graft prosegrinder +graft src graft tests global-exclude .git global-exclude __pycache__ diff --git a/README.md b/README.md new file mode 100644 index 0000000..2f91269 --- /dev/null +++ b/README.md @@ -0,0 +1,129 @@ +# Prosegrinder + +[![Latest PyPI version](https://img.shields.io/pypi/v/prosegrinder.svg)](https://pypi.python.org/pypi/prosegrinder) +[![GitHub Workflow Status](https://github.com/prosegrinder/python-prosegrinder/workflows/Python%20CI/badge.svg?branch=main)](https://github.com/prosegrinder/python-prosegrinder/actions?query=workflow%3A%22Python+CI%22+branch%3Amain) + +A relatively fast, functional prose text counter with readability scoring. + +## Installation + +`prosegrinder` is available on PyPI. Simply install it with `pip`: + +```bash +pip install prosegrinder +``` + +## Usage + +The main use is via the `prosegrinder.Prose` object. + +```python +>>> from prosegrinder import Prose +>>> p = Prose("Some lengthy text that's actual prose, like a novel or article.") +``` + +The Prose object will parse everything down and compute basic statistics, +including word count, sentence count, paragraph count, syllable count, point of +view, dialogue, narrative, and a set of readability scores. All objects and +attributes should be treated as immutable. + +I know this isn't great documentation, but it should be enough to get you going. + +### Command Line Interface + +Prosegrinder now includes a simple CLI for analyzing text in a file: + +```bash +$ prosegrinder --help +Usage: prosegrinder [OPTIONS] FILES... + + Setup the command line interface + +Options: + -i, --indent INTEGER Python pretty-print json indent level. + -s, --save FILENAME File to save output to. + --help Show this message and exit. +``` + +Will provide basic statistics on text from a file or set of files including the +filename and sh256 of text in each file analyzed. Output is json to help +facilitate use in automation:: + +```json +[ + { + "filename": "shortstory.txt", + "statistics": { + "sha256": "5b756dea7c7f0088ff3692e402466af7f4fc493fa357c1ae959fa4493943fc03", + "word_character_count": 7008, + "phone_count": 5747, + "syllable_count": 2287, + "word_count": 1528, + "sentence_count": 90, + "paragraph_count": 77, + "complex_word_count": 202, + "long_word_count": 275, + "pov_word_count": 113, + "first_person_word_count": 8, + "second_person_word_count": 74, + "third_person_word_count": 31, + "pov": "first", + "readability_scores": { + "automated_readability_index": 0.281, + "coleman_liau_index": 9.425, + "flesch_kincaid_grade_level": 8.693, + "flesch_reading_ease": 62.979, + "gunning_fog_index": 12.079, + "linsear_write": 10.733, + "lix": 34.975, + "rix": 3.056, + "smog": 11.688 + } + } + }, + { + "filename": "copyright.txt", + "statistics": { + "sha256": "553bfd087a2736e4bbe2f312e3d3a5b763fb57caa54e3626da03b0fd3f42e017", + "word_character_count": 222, + "phone_count": 169, + "syllable_count": 78, + "word_count": 46, + "sentence_count": 7, + "paragraph_count": 16, + "complex_word_count": 10, + "long_word_count": 12, + "pov_word_count": 1, + "first_person_word_count": 1, + "second_person_word_count": 0, + "third_person_word_count": 0, + "pov": "first", + "readability_scores": { + "automated_readability_index": 1.404, + "coleman_liau_index": 8.073, + "flesch_kincaid_grade_level": 6.982, + "flesch_reading_ease": 56.713, + "gunning_fog_index": 11.324, + "linsear_write": 3.714, + "lix": 32.658, + "rix": 1.714, + "smog": 9.957 + } + } + } +] +``` + +### Readability scores + +The set of scores automatically calculated: + +- Automated Readability Index +- Coleman Liau Index +- Flesch Kincaid Grade Level +- Flesch Reading Ease +- Gunning Fog Index +- Linsear Write +- LIX +- RIX +- SMOG diff --git a/README.rst b/README.rst deleted file mode 100644 index 8563aa3..0000000 --- a/README.rst +++ /dev/null @@ -1,135 +0,0 @@ -Prosegrinder -=============== - -.. image:: https://img.shields.io/pypi/v/prosegrinder.svg - :target: https://pypi.python.org/pypi/prosegrinder - :alt: Latest PyPI version - -.. image:: https://github.com/prosegrinder/python-prosegrinder/workflows/Python%20CI/badge.svg?branch=main - :target: https://github.com/prosegrinder/python-prosegrinder/actions?query=workflow%3A%22Python+CI%22+branch%3Amain - :alt: GitHub Workflow Status - -.. image:: https://app.codacy.com/project/badge/Grade/fbb22c1d33a34aa3bee095fc3ff62bc9 - :target: https://www.codacy.com/gh/prosegrinder/python-prosegrinder?utm_source=github.com&utm_medium=referral&utm_content=prosegrinder/python-prosegrinder&utm_campaign=Badge_Grade - :alt: Latest Codacy Coverage Report - -A relatively fast, functional prose text counter with readability scoring. - -Installation ------------- - -``prosegrinder`` is available on PyPI. Simply install it with ``pip``:: - - $ pip install prosegrinder - -Usage ------ - -The main use is via the prosegrinder.Prose object. - - >>> from prosegrinder import Prose - >>> p = Prose("Some lengthy text that's actual prose, like a novel or article.") - -The Prose object will parse everything down and compute basic staticstics, including word count, -sentence count, paragraph count, syllable count, point of view, dialogue, narrative, and a set -of readabilit scores. All objects and attributes should be treated as immutable. - -I know this isn't great documentation, but it should be enough to get you going. - -Command Line Interaface -~~~~~~~~~~~~~~~~~~~~~~~ - -Prosegrinder now includes a simple CLI for analyzing text in a file::: - - Usage: prosegrinder [OPTIONS] FILES... - - Setup the command line interface - - Options: - -i, --indent INTEGER Python pretty-print json indent level. - -s, --save FILENAME File to save output to. - --help Show this message and exit. - -Will provide basic statistics on text from a file or set of files including the filename and sh256 of text in each file analyzed. Output is json to help facilitate use in automation::: - - [ - { - "filename": "shortstory.txt", - "statistics": { - "sha256": "5b756dea7c7f0088ff3692e402466af7f4fc493fa357c1ae959fa4493943fc03", - "word_character_count": 7008, - "phone_count": 5747, - "syllable_count": 2287, - "word_count": 1528, - "sentence_count": 90, - "paragraph_count": 77, - "complex_word_count": 202, - "long_word_count": 275, - "pov_word_count": 113, - "first_person_word_count": 8, - "second_person_word_count": 74, - "third_person_word_count": 31, - "pov": "first", - "readability_scores": { - "automated_readability_index": 0.281, - "coleman_liau_index": 9.425, - "flesch_kincaid_grade_level": 8.693, - "flesch_reading_ease": 62.979, - "gunning_fog_index": 12.079, - "linsear_write": 10.733, - "lix": 34.975, - "rix": 3.056, - "smog": 11.688 - } - } - }, - { - "filename": "copyright.txt", - "statistics": { - "sha256": "553bfd087a2736e4bbe2f312e3d3a5b763fb57caa54e3626da03b0fd3f42e017", - "word_character_count": 222, - "phone_count": 169, - "syllable_count": 78, - "word_count": 46, - "sentence_count": 7, - "paragraph_count": 16, - "complex_word_count": 10, - "long_word_count": 12, - "pov_word_count": 1, - "first_person_word_count": 1, - "second_person_word_count": 0, - "third_person_word_count": 0, - "pov": "first", - "readability_scores": { - "automated_readability_index": 1.404, - "coleman_liau_index": 8.073, - "flesch_kincaid_grade_level": 6.982, - "flesch_reading_ease": 56.713, - "gunning_fog_index": 11.324, - "linsear_write": 3.714, - "lix": 32.658, - "rix": 1.714, - "smog": 9.957 - } - } - } - ] - - - - - -Readbility scores -~~~~~~~~~~~~~~~~~ - -The set of scores automatically calculated: - -* Automated Readability Index -* Coleman Liau Index -* Flesch Kincaid Grade Level -* Flesch Reading Ease -* Gunning Fog Index -* Linsear Write -* LIX -* RIX -* SMOG diff --git a/prosegrinder/VERSION b/prosegrinder/VERSION deleted file mode 100644 index 3a3cd8c..0000000 --- a/prosegrinder/VERSION +++ /dev/null @@ -1 +0,0 @@ -1.3.1 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..374b58c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,6 @@ +[build-system] +requires = [ + "setuptools>=42", + "wheel" +] +build-backend = "setuptools.build_meta" diff --git a/setup.cfg b/setup.cfg index 56a9af2..bf26be8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,49 @@ [metadata] -license_files = LICENSE +name = prosegrinder +version = file: src/prosegrinder/VERSION +author = David L. Day +author_email = dday376@gmail.com +description = "A text analytics library for prose fiction." +long_description = file: README.md +long_description_content_type = text/markdown +license_file = LICENSE +url = https://github.com/prosegrinder/python-prosegrinder +project_urls = + Bug Tracker = https://github.com/prosegrinder/python-prosegrinder/issues +classifiers = + Intended Audience :: Developers + License :: OSI Approved :: GNU General Public License v3 (GPLv3) + Natural Language :: English + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + +[options] +python_requires = >= 2.7 +package_dir = + = src +packages = find: +include_package_data = True +setup_requires = + setuptools +install_requires = + cmudict>=1.0.0 + narrative>=1.0.0 + pointofview>=1.0.0 + syllables>=1.0.0 + click>=8.0.1 +build_requires = + build +tests_requires = + pytest + +[options.packages.find] +where = src [bdist_wheel] -universal = 1 +universal = true + +[sdist] +formats = zip, gztar diff --git a/setup.py b/setup.py index 2128ee4..6068493 100644 --- a/setup.py +++ b/setup.py @@ -1,52 +1,3 @@ -# -*- coding: utf-8 -*- +from setuptools import setup -from os import path - -from setuptools import find_packages, setup - -# Version -with open(path.join(path.dirname(__file__), "prosegrinder", "VERSION")) as version_file: - VERSION = version_file.read().strip() -# Long Description -with open(path.join(path.dirname(__file__), "README.rst")) as readme_file: - LONG_DESCRIPTION = readme_file.read() - -setup( - name="prosegrinder", - version=VERSION, - description="A text analytics library for prose fiction.", - long_description=LONG_DESCRIPTION, - author="David L. Day", - author_email="dday376@gmail.com", - url="https://github.com/prosegrinder/python-prosegrinder", - packages=find_packages(include=["prosegrinder", "prosegrinder.*"]), - entry_points={ - "console_scripts": [ - "prosegrinder = prosegrinder.__main__:cli", - ], - }, - package_dir={"prosegrinder": "prosegrinder"}, - package_data={ - "": ["LICENSE", "*.rst", "MANIFEST.in"], - }, - include_package_data=True, - classifiers=[ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", - "Natural Language :: English", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.5", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - ], - install_requires=[ - "cmudict>=1.0.0", - "narrative>=1.0.0", - "pointofview>=1.0.0", - "syllables>=1.0.0", - "click>=8.0.1", - ], - python_requires=">=3.6", -) +setup() diff --git a/src/prosegrinder/VERSION b/src/prosegrinder/VERSION new file mode 100644 index 0000000..1892b92 --- /dev/null +++ b/src/prosegrinder/VERSION @@ -0,0 +1 @@ +1.3.2 diff --git a/prosegrinder/__init__.py b/src/prosegrinder/__init__.py similarity index 100% rename from prosegrinder/__init__.py rename to src/prosegrinder/__init__.py diff --git a/prosegrinder/__main__.py b/src/prosegrinder/__main__.py similarity index 100% rename from prosegrinder/__main__.py rename to src/prosegrinder/__main__.py diff --git a/prosegrinder/dictionary.py b/src/prosegrinder/dictionary.py similarity index 100% rename from prosegrinder/dictionary.py rename to src/prosegrinder/dictionary.py diff --git a/prosegrinder/fragment.py b/src/prosegrinder/fragment.py similarity index 100% rename from prosegrinder/fragment.py rename to src/prosegrinder/fragment.py diff --git a/prosegrinder/fragment_container.py b/src/prosegrinder/fragment_container.py similarity index 100% rename from prosegrinder/fragment_container.py rename to src/prosegrinder/fragment_container.py diff --git a/prosegrinder/paragraph.py b/src/prosegrinder/paragraph.py similarity index 100% rename from prosegrinder/paragraph.py rename to src/prosegrinder/paragraph.py diff --git a/prosegrinder/prose.py b/src/prosegrinder/prose.py similarity index 100% rename from prosegrinder/prose.py rename to src/prosegrinder/prose.py diff --git a/prosegrinder/readability_scores.py b/src/prosegrinder/readability_scores.py similarity index 100% rename from prosegrinder/readability_scores.py rename to src/prosegrinder/readability_scores.py diff --git a/prosegrinder/sentence.py b/src/prosegrinder/sentence.py similarity index 100% rename from prosegrinder/sentence.py rename to src/prosegrinder/sentence.py diff --git a/prosegrinder/word.py b/src/prosegrinder/word.py similarity index 100% rename from prosegrinder/word.py rename to src/prosegrinder/word.py diff --git a/tests/test_cli.py b/tests/test_cli.py index ca8440c..d82d126 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -9,7 +9,7 @@ with open(TXT_FILENAME) as txt_file: TXT_CONTENTS = txt_file.read() -COPYRIGHT_FILE = path.join(path.dirname(__file__), 'resources/copyright.txt') +COPYRIGHT_FILE = path.join(path.dirname(__file__), "resources/copyright.txt") with open(COPYRIGHT_FILE) as copyright_file: COPYRIGHT_CONTENTS = copyright_file.read() @@ -22,6 +22,6 @@ def test_cli_defaults(): with runner.isolated_filesystem(): shutil.copyfile(TXT_FILENAME, "shortstory.txt") shutil.copyfile(COPYRIGHT_FILE, "copyright.txt") - result = runner.invoke(main.cli, ['shortstory.txt', 'copyright.txt']) + result = runner.invoke(main.cli, ["shortstory.txt", "copyright.txt"]) assert result.exit_code == 0 assert result.output == JSON_CONTENTS