diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 71997c6..1534e18 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -1,7 +1,7 @@ # This workflow will install Python dependencies, run tests and lint with a variety of Python versions # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python -name: Python package +name: Build and Test on: push diff --git a/.gitignore b/.gitignore index 692b66b..1db8f7e 100644 --- a/.gitignore +++ b/.gitignore @@ -90,5 +90,4 @@ ENV/ /tests/tests.config /tests/creds.json -/travis.secrets.tar.gz /.venv diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 38b68c3..0000000 --- a/.travis.yml +++ /dev/null @@ -1,18 +0,0 @@ -language: python -cache: pip -python: -- '2.7' -- '3.7.9' -- '3.8' -- '3.9' -install: -- pip install -U pip -- pip uninstall -y six -- pip install six>=1.12.0 -- if [[ $TRAVIS_PYTHON_VERSION == 2.6* ]]; then pip install --only-binary -e .; else - pip install --prefer-binary -e .; fi -script: python setup.py test -before_install: -- openssl aes-256-cbc -K $encrypted_efe1688938da_key -iv $encrypted_efe1688938da_iv - -in travis.secrets.tar.gz.enc -out travis.secrets.tar.gz -d -- tar xvzf travis.secrets.tar.gz diff --git a/MANIFEST.in b/MANIFEST.in index 792ff63..ada6128 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -10,3 +10,4 @@ recursive-include tests *.example recursive-include tests *.json recursive-include tests *.py exclude tests/creds.json +exclude tests/tests.config diff --git a/README.rst b/README.rst index 07280fd..7840919 100644 --- a/README.rst +++ b/README.rst @@ -4,8 +4,8 @@ gspread-dataframe .. image:: https://badge.fury.io/py/gspread-dataframe.svg :target: https://badge.fury.io/py/gspread-dataframe -.. image:: https://app.travis-ci.com/robin900/gspread-dataframe.svg?branch=master - :target: https://travis-ci.com/robin900/gspread-dataframe +.. image:: https://github.com/robin900/gspread-dataframe/actions/workflows/python-package.yml/badge.svg?branch=master + :target: https://github.com/robin900/gspread-dataframe/actions/workflows/python-package.yml .. image:: https://img.shields.io/pypi/dm/gspread-dataframe.svg :target: https://pypi.org/project/gspread-dataframe @@ -72,7 +72,8 @@ Installation Requirements ~~~~~~~~~~~~ -* Python 2.7, 3+ +* Python 3 only, for releases 4.0.0 and later +* Python 2.7 and 3 for releases prior to 4.0.0 * gspread (>=3.0.0; to use older versions of gspread, use gspread-dataframe releases of 2.1.1 or earlier) * Pandas >= 0.24.0 diff --git a/VERSION b/VERSION index fcdb2e1..c9d9681 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.0.0 +4.0.0b1 diff --git a/gspread_dataframe.py b/gspread_dataframe.py index 7505f18..b39b210 100644 --- a/gspread_dataframe.py +++ b/gspread_dataframe.py @@ -17,7 +17,6 @@ import logging import re from numbers import Real -from six import string_types, ensure_text try: from collections.abc import defaultdict @@ -70,11 +69,9 @@ def _cellrepr(value, allow_formulas, string_escaping): return "" if isinstance(value, Real): return value - if not isinstance(value, string_types): + if not isinstance(value, str): value = str(value) - value = ensure_text(value, encoding='utf-8') - if (not allow_formulas) and value.startswith("="): return "'%s" % value else: diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..7dfaab4 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,62 @@ +[build-system] +requires = ["setuptools", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "gspread-dataframe" +dynamic = ["version"] +description = "Read/write gspread worksheets using pandas DataFrames" +readme = "README.rst" +requires-python = ">=3.0" +license = { file = "LICENSE" } +keywords = ['spreadsheets', 'google-spreadsheets', 'pandas', 'dataframe'] +authors = [{ name = "Robin Thomas", email = "rthomas900@gmail.com" }] +maintainers = [{ name = "Robin Thomas", email = "rthomas900@gmail.com" }] + +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Intended Audience :: Science/Research", + "Topic :: Office/Business :: Financial :: Spreadsheet", + "Topic :: Software Development :: Libraries :: Python Modules", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3" +] + +dependencies = ["gspread>=3.0.0", "pandas>=0.24.0"] + +[project.optional-dependencies] +dev = [ +"gitchangelog", +"Sphinx", +"Sphinx-PyPI-upload3", +"twine", +"pytest", +"oauth2client" +] + +test = [ +"pytest", +"oauth2client", +"pandas", +"tox" +] + +[project.urls] +"Homepage" = "https://github.com/robin900/gspread-dataframe" +"Bug Reports" = "https://github.com/robin900/gspread-dataframe/issues" +"Source" = "https://github.com/robin900/gspread-dataframe/" + +[tool.setuptools.dynamic] +version = {file = "VERSION"} + +[tool.coverage.report] +fail_under = 90 +show_missing = true +exclude_lines = [ + 'pragma: no cover', + '\.\.\.', + 'if TYPE_CHECKING:', + "if __name__ == '__main__':", +] + diff --git a/requirements-dev.txt b/requirements-dev.txt deleted file mode 100644 index d436ea5..0000000 --- a/requirements-dev.txt +++ /dev/null @@ -1,6 +0,0 @@ -oauth2client -gitchangelog -Sphinx -Sphinx-PyPI-upload3 -twine -wheel diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index d6443c8..0000000 --- a/setup.cfg +++ /dev/null @@ -1,15 +0,0 @@ -[bdist_wheel] -universal = 1 - -[metadata] -license_file=LICENSE - -[build_sphinx] -source-dir = docs -build-dir = docs/_build -all_files = 1 - -[upload_sphinx] -upload-dir = docs/_build/html -repository = https://pypi.python.org/pypi - diff --git a/setup.py b/setup.py deleted file mode 100644 index e0d3773..0000000 --- a/setup.py +++ /dev/null @@ -1,52 +0,0 @@ -try: - from setuptools import setup -except ImportError: - from distutils.core import setup - -import os.path -import sys - -PY3 = sys.version_info >= (3, 0) - -with open(os.path.join(os.path.dirname(__file__), 'VERSION'), 'rb') as f: - VERSION = f.read() - if PY3: - VERSION = VERSION.decode('utf8') - VERSION = VERSION.strip() - -with open(os.path.join(os.path.dirname(__file__), 'README.rst'), 'rb') as f: - long_description = f.read() - if PY3: - long_description = long_description.decode('utf8') - -setup( - name='gspread-dataframe', - version=VERSION, - py_modules=['gspread_dataframe'], - test_suite='tests', - install_requires=[ - 'gspread>=3.0.0', - 'pandas>=0.24.0', - 'six>=1.12.0' - ], - tests_require=['oauth2client'] + ([] if PY3 else ['mock']), - description='Read/write gspread worksheets using pandas DataFrames', - long_description=long_description, - author='Robin Thomas', - author_email='rthomas900@gmail.com', - license='MIT', - url='https://github.com/robin900/gspread-dataframe', - keywords=['spreadsheets', 'google-spreadsheets', 'pandas', 'dataframe'], - classifiers=[ - "Programming Language :: Python", - "Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", - "Development Status :: 5 - Production/Stable", - "Intended Audience :: Developers", - "Intended Audience :: Science/Research", - "Topic :: Office/Business :: Financial :: Spreadsheet", - "Topic :: Software Development :: Libraries :: Python Modules" - ], - zip_safe=True -) diff --git a/tests/gspread_dataframe_test.py b/tests/gspread_dataframe_test.py index 76dde58..d94bb44 100644 --- a/tests/gspread_dataframe_test.py +++ b/tests/gspread_dataframe_test.py @@ -172,9 +172,10 @@ def test_skiprows(self): self.assertEqual(len(df), 6) def test_squeeze(self): - df = get_as_dataframe(self.sheet, usecols=[0], squeeze=True) - self.assertTrue(isinstance(df, pd.Series)) - self.assertEqual(len(df), 9) + if pd.__version__ < '2.0.0': + df = get_as_dataframe(self.sheet, usecols=[0], squeeze=True) + self.assertTrue(isinstance(df, pd.Series)) + self.assertEqual(len(df), 9) def test_converters_datetime(self): df = get_as_dataframe( @@ -218,7 +219,7 @@ def test_parse_dates_custom_parser(self): df = get_as_dataframe( self.sheet, parse_dates=[4], - date_parser=lambda x: datetime.strptime(x, "%Y-%m-%d") if x is not np.nan else None + date_format="%Y-%m-%d" ) self.assertEqual(df["Date Column"][0], datetime(2017, 3, 4)) @@ -289,6 +290,11 @@ def test_write_basic(self): CELL_LIST_STRINGIFIED, value_input_option="USER_ENTERED" ) + def test_write_empty_df_no_updates(self): + df = pd.DataFrame.from_records([]) + set_with_dataframe(self.sheet, df) + self.sheet.update_cells.assert_not_called() + def test_include_index_false(self): df = get_as_dataframe(self.sheet, na_filter=False) df_index = df.set_index("Thingy") diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..3a4ed0f --- /dev/null +++ b/tox.ini @@ -0,0 +1,25 @@ +[tox] +env_list = + 3.8 + 3.13 +minversion = 4.24.2 + +[testenv] +description = run the tests with pytest +package = wheel +wheel_build_env = .pkg +deps = + pytest>=6 + coverage + oauth2client + !3.8: pandas>=2.0.0 + 3.8: pandas<2.0.0 +commands = + coverage erase + coverage run -m pytest {tty:--color=yes} tests/gspread_dataframe_test.py tests/gspread_dataframe_integration.py {posargs} + coverage report --omit='tests/*' + +[gh-actions] +python = + 3.8: py38 + 3.13: py313 diff --git a/travis.secrets.tar.gz.enc b/travis.secrets.tar.gz.enc deleted file mode 100644 index 13be0aa..0000000 Binary files a/travis.secrets.tar.gz.enc and /dev/null differ