diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 157781f..ecced4a 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -5,42 +5,42 @@ name: Python package on: push: - branches: [ master ] + branches: [master] pull_request: - branches: [ master ] + branches: [master] jobs: build: strategy: matrix: - python-version: [3.6, 3.7, 3.8, 3.9] + python-version: ["3.7", "3.8", "3.9", "3.10"] platform: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.platform }} steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python3 -m pip install --upgrade pip - python3 setup.py install - python3 -m pip install -r requirements-dev.txt - - name: Lint with flake8 - run: | - # stop the build if there are Python syntax errors or undefined names - flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - - name: Lint with black - run: | - black --check mapillary_tools tests - - name: Type check with mypy - run: | - mypy ptwit.py tests.py - - name: Test with pytest - run: | - pytest tests.py + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python3 -m pip install --upgrade pip + python3 setup.py install + python3 -m pip install -r requirements-dev.txt + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Lint with black + run: | + black --check ptwit.py + - name: Type check with mypy + run: | + mypy ptwit.py tests.py + - name: Test with pytest + run: | + pytest tests.py diff --git a/MANIFEST.in b/MANIFEST.in index a5021c6..8da04a0 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,3 @@ include README.rst include LICENSE +include requirements.txt diff --git a/ptwit.py b/ptwit.py index 2671a1d..ee58e41 100644 --- a/ptwit.py +++ b/ptwit.py @@ -1,26 +1,24 @@ #!/usr/bin/env python3 +import configparser +import json import os +import re import sys -import errno -from functools import update_wrapper +import typing as T from datetime import datetime -from string import Formatter -import json -import re -import configparser +from functools import update_wrapper from html import unescape as html_unescape -from urllib.parse import parse_qsl -import typing as T +from string import Formatter -import twitter import click +import twitter from click_default_group import DefaultGroup from requests_oauthlib import OAuth1Session from requests_oauthlib.oauth1_session import TokenRequestDenied -__version__ = "0.3" +__VERSION__ = "0.3" MAX_COUNT = 200 @@ -161,17 +159,6 @@ def save(self, filename=None) -> "TwitterConfig": return self -# http://stackoverflow.com/a/600612/114833 -def mkdir(path: str) -> None: - try: - os.makedirs(path) - except OSError as exc: - if exc.errno == errno.EEXIST and os.path.isdir(path): - pass - else: - raise - - @click.group(cls=DefaultGroup, default="timeline", default_if_no_args=True) @click.option("--account", "-a", help="Use this account instead of the default one.") @click.option( @@ -187,7 +174,7 @@ def mkdir(path: str) -> None: @click.pass_context def ptwit(ctx: click.Context, account: T.Optional[str], format: str) -> None: config_dir = click.get_app_dir("ptwit") - mkdir(config_dir) + os.makedirs(config_dir, exist_ok=True) config = TwitterConfig(os.path.join(config_dir, "ptwit.conf")) if account is None: @@ -503,7 +490,7 @@ def read_text(words: T.List[str]) -> str: text = " ".join(words) click.confirm(f'Post "{text}"?', abort=True) else: - text = click.edit() + text = click.edit() or "" return text diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..2c7bd3e --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +python-twitter +click-default-group +click +requests_oauthlib diff --git a/setup.py b/setup.py index a59f37e..06d6923 100644 --- a/setup.py +++ b/setup.py @@ -1,40 +1,59 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- -try: - from setuptools import setup -except ImportError: - from distutils.core import setup +import os +from setuptools import setup + + +here = os.path.abspath(os.path.dirname(__file__)) def readme(): - with open('README.rst') as f: + with open("README.rst") as f: return f.read() -setup(name='ptwit', - version='0.3', - description='A simple twitter command line client', - long_description=readme(), - classifiers=[ - 'Development Status :: 5 - Beta', - 'License :: OSI Approved :: MIT License', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Environment :: Console', - 'Intended Audience :: End Users/Desktop', - 'Topic :: Utilities'], - url='https://github.com/ptpt/ptwit', - author='Tao Peng', - author_email='ptpttt+ptwit@gmail.com', - keywords='twitter, command-line, client', - license='MIT', - py_modules=['ptwit'], - install_requires=['python-twitter', 'click-default-group', 'click'], - entry_points=''' +def read_requirements(): + with open("requirements.txt") as fp: + return [row.strip() for row in fp if row.strip()] + + +about: dict = {} +with open(os.path.join(here, "ptwit.py"), "r") as f: + while True: + line = f.readline() + if not line: + break + if line.startswith("__VERSION__"): + exec(line, about) + break + + +setup( + name="ptwit", + version=about["__VERSION__"], + description="A simple twitter command line client", + long_description=readme(), + classifiers=[ + "Development Status :: 5 - Beta", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Environment :: Console", + "Intended Audience :: End Users/Desktop", + "Topic :: Utilities", + ], + url="https://github.com/ptpt/ptwit", + author="Tao Peng", + author_email="ptpttt+ptwit@gmail.com", + keywords="twitter, command-line, client", + license="MIT", + py_modules=["ptwit"], + install_requires=read_requirements(), + entry_points=""" [console_scripts] ptwit=ptwit:cli - ''', - zip_safe=False) + """, + zip_safe=False, +) diff --git a/tests.py b/tests.py index 81512ae..c289acb 100644 --- a/tests.py +++ b/tests.py @@ -6,70 +6,63 @@ class TestTwitterConfig(unittest.TestCase): - def setUp(self): - _, self.filename = tempfile.mkstemp() - - def tearDown(self): - os.remove(self.filename) - def test_open(self): - filename = tempfile.mktemp() - # Create if config file does not exist - config = TwitterConfig(filename) - self.assertFalse(os.path.isfile(filename)) - # If the path is a directory? - dirname = tempfile.mkdtemp() - config = TwitterConfig(dirname) - self.assertRaises(IOError, config.save) - os.removedirs(dirname) + TwitterConfig("./hello") + self.assertFalse(os.path.exists("./hello")) def test_set(self): - config = TwitterConfig(self.filename) - config.set("option", "value") # Save hello=world to general section - config.set("name", "tao", account="Tao") - config.set("name", "mian", account="Mian") - self.assertEqual(config.config.items("general"), [("option", "value")]) - self.assertEqual(config.config.items("Tao"), [("name", "tao")]) - self.assertEqual(config.config.items("Mian"), [("name", "mian")]) + with tempfile.NamedTemporaryFile() as tmp: + config = TwitterConfig(tmp.name) + config.set("option", "value") # Save hello=world to general section + config.set("name", "tao", account="Tao") + config.set("name", "mian", account="Mian") + self.assertEqual(config.config.items("general"), [("option", "value")]) + self.assertEqual(config.config.items("Tao"), [("name", "tao")]) + self.assertEqual(config.config.items("Mian"), [("name", "mian")]) def test_get(self): - config = TwitterConfig(self.filename) - config.set("option", "value") - config.set("format", "json", account="Tao") - self.assertEqual(config.get("option"), "value") - self.assertEqual(config.get("format", account="Tao"), "json") + with tempfile.NamedTemporaryFile() as tmp: + config = TwitterConfig(tmp.name) + config.set("option", "value") + config.set("format", "json", account="Tao") + self.assertEqual(config.get("option"), "value") + self.assertEqual(config.get("format", account="Tao"), "json") def test_unset(self): - config = TwitterConfig(self.filename) - config.set("option", "value") - config.set("format", "json", account="Tao") - config.unset("format", account="Tao") - config.unset("option") - self.assertIsNone(config.get("format", account="Tao")) - self.assertIsNone(config.get("option")) + with tempfile.NamedTemporaryFile() as tmp: + config = TwitterConfig(tmp.name) + config.set("option", "value") + config.set("format", "json", account="Tao") + config.unset("format", account="Tao") + config.unset("option") + self.assertIsNone(config.get("format", account="Tao")) + self.assertIsNone(config.get("option")) def test_remove_account(self): - config = TwitterConfig(self.filename) - config.set("option", "value", account="Tao") - config.remove_account("Tao") + with tempfile.NamedTemporaryFile() as tmp: + config = TwitterConfig(tmp.name) + config.set("option", "value", account="Tao") + config.remove_account("Tao") def test_list_account(self): - config = TwitterConfig(self.filename) - self.assertEqual(config.list_accounts(), []) - config.set("option", "value") - config.set("option", "value", account="Tao") - self.assertEqual(config.list_accounts(), ["Tao"]) + with tempfile.NamedTemporaryFile() as tmp: + config = TwitterConfig(tmp.name) + self.assertEqual(config.list_accounts(), []) + config.set("option", "value") + config.set("option", "value", account="Tao") + self.assertEqual(config.list_accounts(), ["Tao"]) def test_save(self): - config = TwitterConfig(self.filename) - config.set("option", "value") - config.set("name", "Tao", account="Tao") - config.save() - with open(self.filename) as fp: - content = fp.read() - self.assertTrue(content.find("general")) - self.assertTrue(content.find("Tao")) - self.assertTrue(content.find("name")) + with tempfile.NamedTemporaryFile() as tmp: + config = TwitterConfig(tmp.name) + config.set("option", "value") + config.set("name", "Tao", account="Tao") + config.save() + with open(tmp.name) as fp: + content = fp.read() + self.assertTrue(content.find("general")) + self.assertTrue(content.find("Tao")) + self.assertTrue(content.find("name")) if __name__ == "__main__":