Compare commits

...

624 commits

Author SHA1 Message Date
Hirad 88bb8ea789 Update README.md
Some checks failed
Release drafter / update_release_draft (push) Has been cancelled
Deploy / Build & verify package (push) Has been cancelled
Deploy / Publish in-dev package to test.pypi.org (push) Has been cancelled
Deploy / Publish released package to pypi.org (push) Has been cancelled
Lint / lint (push) Has been cancelled
Test / test (ubuntu-latest, 3.10) (push) Has been cancelled
Test / test (ubuntu-latest, 3.11) (push) Has been cancelled
Test / test (ubuntu-latest, 3.12) (push) Has been cancelled
Test / test (ubuntu-latest, 3.13) (push) Has been cancelled
Test / test (ubuntu-latest, 3.8) (push) Has been cancelled
Test / test (ubuntu-latest, 3.9) (push) Has been cancelled
Test / test (ubuntu-latest, pypy3.10) (push) Has been cancelled
Test / Test successful (push) Has been cancelled
2024-07-07 09:16:54 +03:30
Hirad 6ae051157f Update README.md
Some checks are pending
Deploy / Build & verify package (push) Waiting to run
Deploy / Publish in-dev package to test.pypi.org (push) Blocked by required conditions
Deploy / Publish released package to pypi.org (push) Blocked by required conditions
Lint / lint (push) Waiting to run
Release drafter / update_release_draft (push) Waiting to run
Test / test (ubuntu-latest, 3.10) (push) Waiting to run
Test / test (ubuntu-latest, 3.11) (push) Waiting to run
Test / test (ubuntu-latest, 3.12) (push) Waiting to run
Test / test (ubuntu-latest, 3.13) (push) Waiting to run
Test / test (ubuntu-latest, 3.8) (push) Waiting to run
Test / test (ubuntu-latest, 3.9) (push) Waiting to run
Test / test (ubuntu-latest, pypy3.10) (push) Waiting to run
Test / Test successful (push) Blocked by required conditions
2024-07-07 09:16:25 +03:30
Hirad c260d7b83f change libre.fm to music.lonestar.it
Some checks are pending
Deploy / Build & verify package (push) Waiting to run
Deploy / Publish in-dev package to test.pypi.org (push) Blocked by required conditions
Deploy / Publish released package to pypi.org (push) Blocked by required conditions
Lint / lint (push) Waiting to run
Release drafter / update_release_draft (push) Waiting to run
Test / test (ubuntu-latest, 3.10) (push) Waiting to run
Test / test (ubuntu-latest, 3.11) (push) Waiting to run
Test / test (ubuntu-latest, 3.12) (push) Waiting to run
Test / test (ubuntu-latest, 3.13) (push) Waiting to run
Test / test (ubuntu-latest, 3.8) (push) Waiting to run
Test / test (ubuntu-latest, 3.9) (push) Waiting to run
Test / test (ubuntu-latest, pypy3.10) (push) Waiting to run
Test / Test successful (push) Blocked by required conditions
2024-07-07 09:03:18 +03:30
Hugo van Kemenade 25904371de
[pre-commit.ci] pre-commit autoupdate (#458) 2024-07-02 05:34:35 -06:00
Hugo van Kemenade 184d0328a9 Configure pyproject-fmt to add Python 3.13 classifier 2024-07-02 14:29:20 +03:00
pre-commit-ci[bot] aceaa69c9a
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.4.4 → v0.5.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.4...v0.5.0)
- [github.com/asottile/blacken-docs: 1.16.0 → 1.18.0](https://github.com/asottile/blacken-docs/compare/1.16.0...1.18.0)
- [github.com/python-jsonschema/check-jsonschema: 0.28.4 → 0.28.6](https://github.com/python-jsonschema/check-jsonschema/compare/0.28.4...0.28.6)
- [github.com/rhysd/actionlint: v1.7.0 → v1.7.1](https://github.com/rhysd/actionlint/compare/v1.7.0...v1.7.1)
- [github.com/tox-dev/pyproject-fmt: 1.7.0 → 2.1.3](https://github.com/tox-dev/pyproject-fmt/compare/1.7.0...2.1.3)
2024-07-01 17:24:06 +00:00
Hugo van Kemenade de737fb4ee
Update config (#457) 2024-05-22 22:50:30 +03:00
Hugo van Kemenade 6f97f93dcc Update config 2024-05-22 22:46:47 +03:00
Hugo van Kemenade a23e1c5181
Fix expected result in test and refactor (#456) 2024-05-22 22:38:49 +03:00
Hugo van Kemenade 35b264bae4 Refactor 2024-05-22 22:29:05 +03:00
Hugo van Kemenade 353e32bd6b Fix expected result in test 2024-05-22 22:26:42 +03:00
Hugo van Kemenade 0fa96932a4
Update example_test_pylast.yaml Link in README.md (#455) 2024-05-22 22:08:13 +03:00
pre-commit-ci[bot] 90c3614d6a [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2024-05-22 05:10:35 +00:00
Christian McKinnon fa68aa4ae8 Update example_test_pylast.yaml Link in README.md 2024-05-22 11:48:48 +07:00
Hugo van Kemenade 8a26cf88d8
[pre-commit.ci] pre-commit autoupdate (#453) 2024-04-01 20:41:01 +03:00
pre-commit-ci[bot] 82cb504871
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.2.0 → v0.3.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.2.0...v0.3.4)
- [github.com/psf/black-pre-commit-mirror: 24.1.1 → 24.3.0](https://github.com/psf/black-pre-commit-mirror/compare/24.1.1...24.3.0)
2024-04-01 17:16:48 +00:00
Hugo van Kemenade 9d4283a924
Update github-actions (#450) 2024-03-01 08:06:49 +02:00
Hugo van Kemenade e4d2ebc4a0
Update test.yml 2024-03-01 08:02:36 +02:00
renovate[bot] d5f1c3d3ac
Update github-actions 2024-03-01 01:13:50 +00:00
Hugo van Kemenade e737ae2f34
Add support for Python 3.13 (#448) 2024-02-05 21:35:25 +02:00
Hugo van Kemenade f4547a5821 Add Prettier to pre-commit 2024-02-05 21:27:22 +02:00
Hugo van Kemenade a14a50a333 Scrutinizer was removed in 2019 2024-02-05 21:27:22 +02:00
Hugo van Kemenade 77d1b0009c Add support for Python 3.13 2024-02-05 21:27:22 +02:00
Hugo van Kemenade d505d57fc4
Replace Flake8 with Ruff (#447) 2024-02-05 20:53:51 +02:00
Hugo van Kemenade 3890cb4c04 Add {envpython} and --cov-report html, multiline for clarity 2024-02-04 22:09:24 +02:00
Hugo van Kemenade 5bccda1102 Update config 2024-02-04 22:06:20 +02:00
Hugo van Kemenade 36d89a69e8 Replace Flake8 with Ruff 2024-02-04 22:06:20 +02:00
Hugo van Kemenade a28dea1158
Update github-actions (#446) 2024-02-04 21:10:29 +02:00
Hugo van Kemenade 6c888343c8
Pin codecov/codecov-action to v3.1.5 2024-02-04 12:00:33 -07:00
renovate[bot] ffebde28e2
Update github-actions 2024-02-04 18:59:28 +00:00
Hugo van Kemenade befb5aeceb
Double read timeout to fix 'The read operation timed out' (#445) 2024-02-04 20:58:44 +02:00
Hugo van Kemenade 370ff77f21 Double read timeout to fix 'The read operation timed out'
5 seconds is the default
2024-01-22 09:23:41 +02:00
Hugo van Kemenade cdfc23b5e4
Update github-actions (#443)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-15 19:24:32 +02:00
pre-commit-ci[bot] d5fe263c23
[pre-commit.ci] pre-commit autoupdate (#444)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Hugo van Kemenade <hugovk@users.noreply.github.com>
2024-01-01 10:31:29 -07:00
renovate[bot] e90d717b66
Update github-actions 2024-01-01 01:18:15 +00:00
Eugene Simonov b4c8dc7282
Add type annotations to methods that take timestamp parameter (#442)
Co-authored-by: Eugene Simonov <eugene.simonov@evergen.com.au>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Hugo van Kemenade <hugovk@users.noreply.github.com>
2023-12-29 12:00:10 -07:00
Hugo van Kemenade 6f30559a3a
Fix incorrect docstrings (#439)
Changes proposed in this pull request:
- fix docstrings with inaccurate descriptions

While going through the pyLast documentation, I noticed that a few
functions had inaccurate descriptions. The proper use of these functions
should be intuitive from their names, but I thought it still might be
useful to fix their docstrings. Let me know if there are any issues with
these changes. Thanks!
2023-10-27 07:00:57 +03:00
Mia Bilka a91bac007d Fixed incorrect docstrings 2023-10-26 18:02:01 -07:00
pre-commit-ci[bot] c0f9f4222a
[pre-commit.ci] pre-commit autoupdate (#437)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-10-02 14:16:48 -06:00
renovate[bot] 47872dbb32
Update actions/checkout action to v4 (#436)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [actions/checkout](https://togithub.com/actions/checkout) | action |
major | `v3` -> `v4` |

---

### Release Notes

<details>
<summary>actions/checkout (actions/checkout)</summary>

###
[`v4`](https://togithub.com/actions/checkout/blob/HEAD/CHANGELOG.md#v400)

[Compare Source](https://togithub.com/actions/checkout/compare/v3...v4)

- [Support fetching without the --progress
option](https://togithub.com/actions/checkout/pull/1067)
-   [Update to node20](https://togithub.com/actions/checkout/pull/1436)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "on the first day of the month" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Mend
Renovate](https://www.mend.io/free-developer-tools/renovate/). View
repository job log
[here](https://developer.mend.io/github/pylast/pylast).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4wLjMiLCJ1cGRhdGVkSW5WZXIiOiIzNy4wLjMiLCJ0YXJnZXRCcmFuY2giOiJtYWluIn0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-01 08:37:46 +03:00
pre-commit-ci[bot] 74392c4d71
[pre-commit.ci] pre-commit autoupdate (#434)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Hugo van Kemenade <hugovk@users.noreply.github.com>
2023-08-22 14:22:24 +03:00
Hugo van Kemenade 97eab1719f
Remove default 'cache-dependency-path: pyproject.toml' (#435) 2023-07-24 17:36:32 +03:00
Hugo van Kemenade e4b7af41f9 Remove default 'cache-dependency-path: pyproject.toml'
Committed via https://github.com/asottile/all-repos
2023-07-24 17:33:42 +03:00
Hugo van Kemenade 7f1c90cfea
CI: Replace pypy3.9 with pypy3.10 (#433) 2023-06-18 14:45:26 +03:00
Hugo van Kemenade 68c0197028 CI: Replace pypy3.9 with pypy3.10
Committed via https://github.com/asottile/all-repos
2023-06-18 14:42:29 +03:00
Hugo van Kemenade 9e62e37b1e
Update mheap/github-action-required-labels action to v5 (#432) 2023-06-08 21:24:55 +03:00
Hugo van Kemenade c26c5f86aa Update on the first day of the month 2023-06-08 21:22:05 +03:00
renovate[bot] f7a73aa62f
Update mheap/github-action-required-labels action to v5 2023-06-08 17:29:12 +00:00
Hugo van Kemenade 34e0e54fea
Merge pull request #431 from pylast/deploy 2023-06-06 19:24:16 +03:00
Hugo van Kemenade 02da99f4b0 Use hynek/build-and-inspect-python-package 2023-06-06 19:20:56 +03:00
Hugo van Kemenade 5f302e0813
Merge pull request #430 from pylast/rm-3.7 2023-06-06 19:15:22 +03:00
Hugo van Kemenade 47eda3ea70 Refactor: make helper methods static 2023-06-03 18:10:01 +03:00
Hugo van Kemenade 7da76f49bd Refactor: replace redundant helper methods, no need with pytest 2023-06-03 18:03:05 +03:00
Hugo van Kemenade 1c669d8bb0 Fix test: now returns a png 2023-06-03 17:55:00 +03:00
Hugo van Kemenade 0f59831dc2 Drop support for EOL Python 3.7 2023-06-03 17:41:35 +03:00
Hugo van Kemenade 8d8263ce42
Merge pull request #428 from pylast/deploy 2023-04-18 06:31:29 -06:00
Hugo van Kemenade 07ce433fc0
Merge pull request #427 from pylast/add-3.12 2023-04-18 06:21:36 -06:00
Hugo van Kemenade dc4bd8474c Test newest PyPy 2023-04-18 06:19:54 -06:00
Hugo van Kemenade b05b8454f5 Update pre-commit 2023-04-18 06:09:25 -06:00
Hugo van Kemenade 56fc297371 Publish to PyPI with a Trusted Publisher 2023-04-18 06:08:52 -06:00
Hugo van Kemenade 9f59dd770c Add support for Python 3.12 2023-04-18 06:04:33 -06:00
Hugo van Kemenade f0ea480334
Merge pull request #426 from pylast/pre-commit-ci-update-config 2023-04-18 05:49:37 -06:00
Hugo van Kemenade 165e4761f4
Merge pull request #424 from pylast/all-repos_autofix_fix-deprecated-repository_url
Replace deprecated repository_url with repository-url
2023-04-18 05:34:12 -06:00
Hugo van Kemenade cdb88b9bbb Update pre-commit 2023-04-18 05:28:30 -06:00
pre-commit-ci[bot] 879591e1cc [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2023-04-03 19:10:56 +00:00
pre-commit-ci[bot] 6a7a23cd9a
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/psf/black: 22.12.0 → 23.3.0](https://github.com/psf/black/compare/22.12.0...23.3.0)
- [github.com/asottile/blacken-docs: v1.12.1 → 1.13.0](https://github.com/asottile/blacken-docs/compare/v1.12.1...1.13.0)
- [github.com/pre-commit/pygrep-hooks: v1.9.0 → v1.10.0](https://github.com/pre-commit/pygrep-hooks/compare/v1.9.0...v1.10.0)
- [github.com/tox-dev/pyproject-fmt: 0.4.1 → 0.9.2](https://github.com/tox-dev/pyproject-fmt/compare/0.4.1...0.9.2)
- [github.com/abravalheri/validate-pyproject: v0.10.1 → v0.12.2](https://github.com/abravalheri/validate-pyproject/compare/v0.10.1...v0.12.2)
- [github.com/tox-dev/tox-ini-fmt: 0.5.2 → 1.0.0](https://github.com/tox-dev/tox-ini-fmt/compare/0.5.2...1.0.0)
2023-04-03 19:10:46 +00:00
Hugo van Kemenade 793ae1453c
Merge pull request #425 from pylast/renovate/github-actions
chore(deps): update mheap/github-action-required-labels action to v4
2023-04-02 14:03:18 +03:00
renovate[bot] 111334328e
chore(deps): update mheap/github-action-required-labels action to v4 2023-04-02 09:06:18 +00:00
Hugo van Kemenade 15f0ccfd58 Replace deprecated repository_url with repository-url
Committed via https://github.com/asottile/all-repos
2023-03-19 15:53:12 +02:00
Hugo van Kemenade 94432d62b0
Merge pull request #423 from pylast/all-repos_autofix_all-repos-sed 2023-01-29 13:29:31 +02:00
Hugo van Kemenade dab0a5b661 Bump isort to fix Poetry
Re: https://github.com/PyCQA/isort/pull/2078

Committed via https://github.com/asottile/all-repos
2023-01-29 13:27:16 +02:00
Hugo van Kemenade 7f07babdf4
Merge pull request #421 from ndm13/patch-1
Document how to authenticate with a session key
2023-01-06 09:54:32 +02:00
Hugo van Kemenade f5ea06c6c9
Include "import pylast" in both blocks 2023-01-06 09:49:52 +02:00
Hugo van Kemenade 4ea1df0930
Merge pull request #420 from pylast/pre-commit-ci-update-config 2023-01-05 18:46:19 +02:00
Hugo van Kemenade e63ecc7bea Autolabel pre-commit PRs with 'changelog: skip' 2023-01-05 18:42:43 +02:00
Hugo van Kemenade 28403386a8 Bump Black 2023-01-05 18:41:16 +02:00
Hugo van Kemenade 8647cbdd48 Make alternative clearer via own code blocks 2023-01-05 18:36:38 +02:00
ndm13 a37ac22e6c
Add code from pylast #407 to readme
Describe authentication with OAuth token
2023-01-02 15:09:16 -05:00
pre-commit-ci[bot] 7861fd55bd
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/pyupgrade: v3.1.0 → v3.3.1](https://github.com/asottile/pyupgrade/compare/v3.1.0...v3.3.1)
- [github.com/psf/black: 22.10.0 → 22.12.0](https://github.com/psf/black/compare/22.10.0...22.12.0)
- [github.com/PyCQA/isort: 5.10.1 → 5.11.4](https://github.com/PyCQA/isort/compare/5.10.1...5.11.4)
- [github.com/PyCQA/flake8: 5.0.4 → 6.0.0](https://github.com/PyCQA/flake8/compare/5.0.4...6.0.0)
- [github.com/pre-commit/pre-commit-hooks: v4.3.0 → v4.4.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.3.0...v4.4.0)
- [github.com/tox-dev/pyproject-fmt: 0.3.5 → 0.4.1](https://github.com/tox-dev/pyproject-fmt/compare/0.3.5...0.4.1)
2023-01-02 18:09:58 +00:00
Hugo van Kemenade 219be9f61a
Merge pull request #419 from pylast/renovate/github-actions 2022-12-31 00:07:48 +02:00
renovate[bot] 8169fad09d
chore(deps): update mheap/github-action-required-labels action to v3 2022-12-30 21:56:31 +00:00
Hugo van Kemenade 0152d98b28
Merge pull request #418 from pylast/all-repos_autofix_add-3.12-dev 2022-11-09 14:00:31 +02:00
Hugo van Kemenade d03e25fc6c Test Python 3.12-dev
Committed via https://github.com/asottile/all-repos
2022-11-09 13:44:07 +02:00
Hugo van Kemenade ce76c03581
Merge pull request #416 from pylast/test-3.11-final
Test on Python 3.11 final
2022-10-25 18:58:49 +03:00
Hugo van Kemenade 7f3518fc1a
Test on Python 3.11 final 2022-10-25 18:32:50 +03:00
Hugo van Kemenade 0560f711c3
Merge pull request #414 from pylast/pre-commit-ci-update-config 2022-10-25 14:53:29 +03:00
pre-commit-ci[bot] 41e0dd604e [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/pyupgrade: v2.34.0 → v2.38.2](https://github.com/asottile/pyupgrade/compare/v2.34.0...v2.38.2)
- [github.com/psf/black: 22.6.0 → 22.8.0](https://github.com/psf/black/compare/22.6.0...22.8.0)
- [github.com/PyCQA/flake8: 4.0.1 → 5.0.4](https://github.com/PyCQA/flake8/compare/4.0.1...5.0.4)
- [github.com/asottile/setup-cfg-fmt: v1.20.1 → v2.0.0](https://github.com/asottile/setup-cfg-fmt/compare/v1.20.1...v2.0.0)
2022-10-25 14:50:00 +03:00
Hugo van Kemenade 8ea5b42d92
Merge pull request #413 from pylast/migrate-packaging 2022-10-25 14:42:39 +03:00
Hugo van Kemenade dbbbcfec44 pyLast 5.1+ supports Python 3.7-3.11 2022-09-26 14:20:35 +03:00
Hugo van Kemenade fc288040a8 Migrate from setuptools + setuptools_scm to hatchling + hatch-vcs 2022-09-26 14:01:16 +03:00
Hugo van Kemenade 98943d606e Migrate from setup.* to pyproject.toml 2022-09-26 11:45:08 +03:00
Hugo van Kemenade 54a9f04f8f
Merge pull request #412 from pylast/fix-test_track_get_duration 2022-09-26 11:35:02 +03:00
Hugo van Kemenade 7f1de76f6e Fix test_track_get_duration 2022-09-26 11:30:50 +03:00
Hugo van Kemenade ece37c4659
Merge pull request #410 from pylast/all-repos_autofix_pypa/gh-action-pypi-publish 2022-07-25 21:44:24 +03:00
Hugo van Kemenade 8a967b52f4 Replace deprecated pypa/gh-action-pypi-publish@master with @release/v1
Committed via https://github.com/asottile/all-repos
2022-07-25 21:21:03 +03:00
Hugo van Kemenade d35eb5220f
Merge pull request #408 from pylast/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2022-07-04 22:10:41 +03:00
pre-commit-ci[bot] aeba21dedb
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/pyupgrade: v2.32.0 → v2.34.0](https://github.com/asottile/pyupgrade/compare/v2.32.0...v2.34.0)
- [github.com/psf/black: 22.3.0 → 22.6.0](https://github.com/psf/black/compare/22.3.0...22.6.0)
- [github.com/pre-commit/pre-commit-hooks: v4.2.0 → v4.3.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.2.0...v4.3.0)
2022-07-04 17:40:19 +00:00
Hugo van Kemenade af71e116e0
Merge pull request #406 from pylast/renovate/github-actions 2022-06-21 20:05:13 +03:00
renovate[bot] 7df369dfff
chore(deps): update mheap/github-action-required-labels action to v2 2022-06-21 16:53:11 +00:00
Hugo van Kemenade 790351928f
Merge pull request #405 from pylast/renovate/github-actions
chore(deps): update actions/setup-python action to v4
2022-06-08 20:01:47 +03:00
Hugo van Kemenade 1e9d7d8c94
A Python version is required for v4 2022-06-08 19:55:48 +03:00
Renovate Bot 139e77707d
chore(deps): update actions/setup-python action to v4 2022-06-08 16:08:22 +00:00
Hugo van Kemenade 8ed1ff2a3e
Merge pull request #404 from pylast/renovate/github-actions
chore(deps): update pre-commit/action action to v3
2022-06-06 07:53:46 +03:00
Renovate Bot 3823d77a35
chore(deps): update pre-commit/action action to v3 2022-06-05 20:27:34 +00:00
Hugo van Kemenade 9f6fcf34fb
Merge pull request #401 from pylast/renovate/github-actions 2022-05-02 18:26:56 +03:00
Renovate Bot 75e2dd5f2e
chore(deps): update github-actions to v3 2022-05-02 15:20:10 +00:00
Hugo van Kemenade 11f70bfee9
Merge pull request #400 from pylast/renovate/configure 2022-05-02 18:19:51 +03:00
Hugo van Kemenade 4fc4a6ad89 Allow combining major bumps for GHA 2022-05-02 18:13:09 +03:00
Hugo van Kemenade 861182253c Move to .github and add labels 2022-05-02 18:13:09 +03:00
Renovate Bot ea421db602 chore(deps): add renovate.json 2022-05-02 18:13:09 +03:00
Hugo van Kemenade d3ba0be1a3
Merge pull request #399 from pylast/add-3.11
Support Python 3.11
2022-05-02 18:13:01 +03:00
Hugo van Kemenade afbafe1e76 Fix test 2022-05-02 15:06:07 +03:00
Hugo van Kemenade dec407d958 Add final 'Test successful' to simplify PR status check requirements 2022-05-02 14:59:08 +03:00
Hugo van Kemenade fa94ed0263 Support Python 3.11 2022-05-02 14:59:08 +03:00
Hugo van Kemenade 5b0c879fa0 Update config 2022-05-02 14:59:08 +03:00
Hugo van Kemenade aefa7cef1b
Merge pull request #395 from pylast/cleanup 2022-04-06 18:05:20 +03:00
Hugo van Kemenade caf0915062
Merge pull request #396 from pylast/pre-commit-ci-update-config 2022-04-04 20:47:36 +03:00
Hugo van Kemenade 2478980ca5 For some reason the earlier track is returning duration=0 2022-04-04 20:44:32 +03:00
pre-commit-ci[bot] c1a8a9455f
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/pyupgrade: v2.31.0 → v2.31.1](https://github.com/asottile/pyupgrade/compare/v2.31.0...v2.31.1)
- [github.com/psf/black: 21.12b0 → 22.3.0](https://github.com/psf/black/compare/21.12b0...22.3.0)
- [github.com/asottile/blacken-docs: v1.12.0 → v1.12.1](https://github.com/asottile/blacken-docs/compare/v1.12.0...v1.12.1)
- [github.com/asottile/setup-cfg-fmt: v1.20.0 → v1.20.1](https://github.com/asottile/setup-cfg-fmt/compare/v1.20.0...v1.20.1)
2022-04-04 17:19:23 +00:00
Hugo van Kemenade 4f37ba41bd
Initialise float as 0.0
And skip Iterator type for now to avoid its complex subscripting
2022-04-03 18:11:42 +03:00
Hugo van Kemenade ac991cbd2c Types and typos 2022-04-03 12:58:44 +03:00
Hugo van Kemenade 14e091c870 autotyping: --annotate-imprecise-magics: add imprecise type annotations for some additional magic methods 2022-04-03 12:49:01 +03:00
Hugo van Kemenade 7b9c73acb7 autotyping: --annotate-magics: add type annotation to certain magic methods 2022-04-03 12:47:58 +03:00
Hugo van Kemenade 54ea354a7a autotyping: --int-param, --float-param, --str-param, --bytes-param: add an annotation to any parameter for which the default is a literal int, float, str, or bytes object 2022-04-03 12:47:10 +03:00
Hugo van Kemenade 5ab3e53a44 autotyping: --bool-param: add a : bool annotation to any function parameter with a default of True or False 2022-04-03 12:46:14 +03:00
Hugo van Kemenade eb4af40d64 autotyping: --scalar-return: add a return annotation to functions that only return literal bool, str, bytes, int, or float objects 2022-04-03 12:45:23 +03:00
Hugo van Kemenade 6c3f3afb3a autotyping: --none-return: add a -> None return type to functions without any return, yield, or raise in their body 2022-04-03 12:45:02 +03:00
Hugo van Kemenade 4e5fe31572 Rename variable e to element 2022-04-03 12:38:16 +03:00
Hugo van Kemenade b0f2f5fe13 For some reason the earlier track is returning duration=0 2022-04-03 12:35:28 +03:00
Hugo van Kemenade 95c8b16564 Upgrade Black to fix Click 2022-04-03 12:35:28 +03:00
Hugo van Kemenade 549437b640 Fix 'a a...' to 'an a...' 2022-04-03 12:33:38 +03:00
Hugo van Kemenade b373de6c68 More f-strings 2022-04-03 12:33:38 +03:00
Hugo van Kemenade 5f8d150652 Remove redundant _get_cache_backend and add some typing 2022-04-03 12:33:38 +03:00
Hugo van Kemenade 83aeaddc43
Merge pull request #394 from pylast/update-logging 2022-04-03 10:57:22 +03:00
Hugo van Kemenade dd8836e59b Logging: log method names at INFO level, also log API return data at DEBUG level 2022-03-03 13:15:26 +02:00
Hugo van Kemenade 5c9509dfc4
Merge pull request #392 from pylast/all-repos_autofix_all-repos-sed 2022-03-01 11:51:54 +02:00
Hugo van Kemenade b726227d5d Upgrade to actions/setup-python@v3
Committed via https://github.com/asottile/all-repos
2022-03-01 11:48:22 +02:00
Hugo van Kemenade f28a74791d
Merge pull request #390 from pylast/fix-album-mbid-none 2022-02-27 20:18:30 +02:00
Hugo van Kemenade fe7484b3ca If album has no MBID, album.get_getmbid() returns None 2022-02-27 16:46:29 +02:00
Hugo van Kemenade 00f92eb436
Merge pull request #391 from pylast/fix-coverage 2022-02-27 16:45:33 +02:00
Hugo van Kemenade f7090f26a0 Output coverage XML for Codecov to upload 2022-02-27 16:38:08 +02:00
Hugo van Kemenade 4ae6c16f57
Merge pull request #379 from pylast/httpx 2022-02-27 16:22:07 +02:00
Hugo van Kemenade 1a45c3b919 Allow setting multiple proxies + some cleanup 2022-02-27 16:18:41 +02:00
Hugo van Kemenade da2e7152ba Update blacken-docs to match main black 2022-02-27 16:18:41 +02:00
Hugo van Kemenade a418f64b15 Simplify _unicode 2022-02-27 16:18:41 +02:00
Hugo van Kemenade 122c870312 Replace _string with str 2022-02-27 16:18:41 +02:00
Hugo van Kemenade 44ade40579 Replace http.client with HTTPX 2022-02-27 16:18:41 +02:00
Hugo van Kemenade 26db2bc68b
Merge pull request #388 from pylast/rm-deprecations 2022-02-27 16:17:06 +02:00
Hugo van Kemenade bb05699252 Remove deprecated is_streamable and is_fulltrack_available 2022-02-27 16:13:04 +02:00
Hugo van Kemenade 7f4bea6f07
Merge pull request #387 from pylast/revert-383-add-3.6 2022-02-27 16:11:20 +02:00
Hugo van Kemenade d610721167 Drop support for Python EOL 3.6 2022-02-27 16:08:33 +02:00
Hugo van Kemenade 6465f4cf51
Update link to deploy action 2022-01-31 12:50:28 +02:00
Hugo van Kemenade bafc3fe673
Merge pull request #385 from pylast/rm-mergify 2022-01-24 22:57:48 +02:00
Hugo van Kemenade b151dd0c93 Remove Mergify, use native GitHub auto-merge instead 2022-01-24 22:54:54 +02:00
Hugo van Kemenade dd869b5183
Merge pull request #384 from pylast/deprecate-streamable 2022-01-24 22:37:16 +02:00
Hugo van Kemenade 3ffe7cf65a test_get_userplaycount now passes 2022-01-24 22:26:16 +02:00
Hugo van Kemenade 1841fb66dc This test now passes, although some other MBID searches are still broken 2022-01-24 22:07:03 +02:00
Hugo van Kemenade d672e89f23 Is an xfail passing unexpectedly? Make it fail 2022-01-24 21:15:11 +02:00
Hugo van Kemenade 3b7cb9c8c7 Deprecate is_streamable and is_fulltrack_available 2022-01-24 19:05:06 +02:00
Hugo van Kemenade e14f51a32a
Merge pull request #383 from pylast/add-3.6 2022-01-24 19:04:27 +02:00
Hugo van Kemenade c63e0a75ef Restore support for Python 3.6 2022-01-24 18:02:15 +02:00
Hugo van Kemenade a204055798
Merge pull request #382 from pylast/remove-invalid-xml-chars-from-response 2022-01-12 22:14:45 +02:00
Hugo van Kemenade 9676714dcf Strip invalid XML characters from response 2022-01-12 13:04:34 +02:00
Hugo van Kemenade 2469a6ea47
Merge pull request #378 from pylast/rm-3.6 2022-01-11 15:35:19 +02:00
Hugo van Kemenade d46aabc372
Merge pull request #380 from pylast/pre-commit-ci-update-config 2022-01-03 19:06:47 +02:00
pre-commit-ci[bot] 129e4392fc
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/pyupgrade: v2.29.1 → v2.31.0](https://github.com/asottile/pyupgrade/compare/v2.29.1...v2.31.0)
- [github.com/psf/black: 21.11b1 → 21.12b0](https://github.com/psf/black/compare/21.11b1...21.12b0)
- [github.com/pre-commit/pre-commit-hooks: v4.0.1 → v4.1.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.0.1...v4.1.0)
- [github.com/tox-dev/tox-ini-fmt: 0.5.1 → 0.5.2](https://github.com/tox-dev/tox-ini-fmt/compare/0.5.1...0.5.2)
2022-01-03 16:58:33 +00:00
Hugo van Kemenade 8b66e69004 Drop support for soon-EOL Python 3.6 2021-11-21 18:43:32 +02:00
Hugo van Kemenade 2966ecfd13
Merge pull request #367 from ChandlerSwift/fix-limit-on-user-top-tracks 2021-11-21 18:30:38 +02:00
Hugo van Kemenade 4d4d167394
Merge pull request #377 from pylast/speedup 2021-11-21 18:21:06 +02:00
Hugo van Kemenade b48fbb4eb8 Speedup: Use faster importlib.metadata for getting version 2021-11-21 18:07:40 +02:00
Hugo van Kemenade d3ee0e4942
Merge pull request #376 from pylast/setup-py-to-cfg 2021-11-21 17:51:06 +02:00
Hugo van Kemenade 25cf4165ea Fix typo 2021-11-21 17:44:55 +02:00
Hugo van Kemenade 754d94374b Use actions/setup-python's pip cache 2021-11-21 17:44:49 +02:00
Hugo van Kemenade b3fb55586c Convert setup.py to static setup.cfg and format with setup-cfg-fmt 2021-11-21 16:42:04 +02:00
Hugo van Kemenade a0bdc3c5ac
Merge pull request #375 from pylast/all-repos_autofix_all-repos-sed 2021-11-10 12:48:44 +02:00
Hugo van Kemenade 3a7c83998f Replace MBID for missing test album with a real one 2021-11-10 11:44:10 +02:00
Hugo van Kemenade ae7d4e3625 Replace deprecated pypy3 with pypy-3.8
Committed via https://github.com/asottile/all-repos
2021-11-10 10:53:05 +02:00
Hugo van Kemenade 7b98775fa0
Merge pull request #374 from pylast/rename-master-to-main 2021-10-19 14:01:07 +03:00
Hugo van Kemenade 05b4ad8c62 Disable the flaky write tests 2021-10-19 13:57:47 +03:00
Hugo van Kemenade c41f831d82 Fix test 2021-10-19 13:17:20 +03:00
Hugo van Kemenade e5b9f2aa19 Add colour to tests 2021-10-19 13:11:21 +03:00
Hugo van Kemenade 9072b98a18 Rename master to main, use 3.10 final, add workflow_dispatch 2021-10-19 13:08:53 +03:00
Hugo van Kemenade 3db88e98ce
Merge pull request #372 from pylast/add-3.10 2021-10-04 20:48:19 +03:00
Hugo van Kemenade 031b3ebbb1 Add support for Python 3.10 2021-10-04 20:36:05 +03:00
Hugo van Kemenade aebbe53a61
Merge pull request #371 from pylast/pre-commit-ci-update-config 2021-10-04 20:12:19 +03:00
pre-commit-ci[bot] 73e3b1b9ed
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/pyupgrade: v2.23.1 → v2.29.0](https://github.com/asottile/pyupgrade/compare/v2.23.1...v2.29.0)
- [github.com/psf/black: 21.7b0 → 21.9b0](https://github.com/psf/black/compare/21.7b0...21.9b0)
- [github.com/asottile/blacken-docs: v1.10.0 → v1.11.0](https://github.com/asottile/blacken-docs/compare/v1.10.0...v1.11.0)
2021-10-04 16:45:32 +00:00
Hugo van Kemenade fd520ad47b
Merge pull request #370 from pylast/pre-commmit 2021-08-02 21:44:01 +03:00
Hugo van Kemenade a850f093f0 track.getInfo with mbid is broken at Last.fm: https://support.last.fm/t/track-getinfo-with-mbid-returns-6-track-not-found/47905 2021-08-02 21:09:28 +03:00
Hugo van Kemenade ddb1b1e501 Fix test 2021-08-02 20:50:21 +03:00
Hugo van Kemenade 72491f7a99 Last.fm now even skips an empty <content/> when no bio 2021-08-02 20:46:56 +03:00
Hugo van Kemenade c8a64dbee9 Test image is now gif 2021-08-02 20:42:20 +03:00
Hugo van Kemenade 20cd3ff475 Update pre-commit and add quarterly autoupdate_schedule 2021-08-02 20:28:45 +03:00
Hugo van Kemenade e193106bde
Merge pull request #369 from tieubinhco/master
Remove artist.shout("<3") in README.md
2021-05-29 10:25:57 +03:00
Tran Tieu Binh 1a35601f51 Remove blank line 2021-05-29 14:24:41 +07:00
Tran Tieu Binh ce2c1e6f76 Remove artist.shout("<3")
There is no shout() method for the artist object.
2021-05-29 13:57:30 +07:00
Hugo van Kemenade a516a44c32 New changes are documented in GH Releases 2021-04-30 22:53:37 +03:00
Hugo van Kemenade 55107d12ba
Merge pull request #358 from kvanzuijlen/feature/fix_for_userloved_userplaycount 2021-04-30 22:19:21 +03:00
Chandler Swift 4e645ca134
Set get_top_tracks limit even if it's None
To get an unlimited number of top tracks, `_get_things` expects
`params['limit']` to be set to `None`. However, this can't happen here
because `None` is falsy.

Fixes #366.
2021-04-27 15:29:26 -05:00
Hugo van Kemenade aad860a222
Add 4.2.0 [CI skip] 2021-03-14 19:37:02 +02:00
Hugo van Kemenade 6fa502ea17 Update release draft title 2021-03-14 18:07:50 +02:00
Hugo van Kemenade 585da81d56
Merge pull request #356 from kvanzuijlen/feature/unsafe_tempfile 2021-03-14 17:55:53 +02:00
Hugo van Kemenade 5e1dc22fea
Merge pull request #363 from pylast/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2021-02-08 20:12:44 +02:00
pre-commit-ci[bot] e87da1efde
[pre-commit.ci] pre-commit autoupdate 2021-02-08 16:28:18 +00:00
Hugo van Kemenade 20692d1c9e
Merge pull request #362 from pylast/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2021-02-01 18:33:23 +02:00
pre-commit-ci[bot] c6e983b579
[pre-commit.ci] pre-commit autoupdate 2021-02-01 16:27:39 +00:00
Koen van Zuijlen ea1f2b42f8 Merge branch 'master' into feature/unsafe_tempfile 2021-01-12 10:19:29 +01:00
Koen van Zuijlen 10803a0a63 Merge branch 'master' into feature/fix_for_userloved_userplaycount 2021-01-12 10:19:24 +01:00
Koen van Zuijlen a41f2e0f36 Merge remote-tracking branch 'pylast/master' 2021-01-12 10:18:59 +01:00
Hugo van Kemenade 26ffcf5ad6
Add 4.1.0 2021-01-04 20:29:53 +02:00
Hugo van Kemenade b7700b58c7
Merge pull request #360 from pylast/updates 2021-01-04 20:25:12 +02:00
Hugo van Kemenade d7ee88ebe2
Merge pull request #359 from pylast/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2021-01-04 18:43:59 +02:00
Hugo van Kemenade 2bf906af17 Update copyright year 2021-01-04 18:42:55 +02:00
Hugo van Kemenade c0fb459458 Update test config 2021-01-04 18:41:16 +02:00
Hugo van Kemenade 6328b9e106 Name lint job after workflow 2021-01-04 18:40:56 +02:00
Hugo van Kemenade 7a235fcc6e Update release config 2021-01-04 18:40:46 +02:00
Hugo van Kemenade f3ee6a71a7 Update label config 2021-01-04 18:40:36 +02:00
pre-commit-ci[bot] 9241a02637
[pre-commit.ci] pre-commit autoupdate 2021-01-04 16:26:38 +00:00
pre-commit-ci[bot] 0c546976b9 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2021-01-02 00:06:16 +00:00
Koen van Zuijlen 6fe9aa632b Fix for user play count and user loved 2021-01-02 00:48:32 +01:00
Koen van Zuijlen 36b2eeb297 Code improvement 2020-12-30 17:12:32 +01:00
Koen van Zuijlen e9bef6db68 Bugfix for caching between sessions 2020-12-30 17:11:38 +01:00
pre-commit-ci[bot] eca1db8622 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2020-12-30 14:59:18 +00:00
Koen van Zuijlen 2d2e73c1bc Fixed unsafe tempfile and fixed some basic problems 2020-12-30 15:56:35 +01:00
Koen van Zuijlen 123a00c5e3 Merge remote-tracking branch 'pylast/master' 2020-12-30 14:10:16 +01:00
Hugo van Kemenade 3fcf45062d Blacken docs 2020-12-30 13:09:06 +02:00
Hugo van Kemenade 6c66279957 Blacken docs 2020-12-30 13:07:03 +02:00
Hugo van Kemenade bf7cd60774 Update formatting, versions and links 2020-12-30 13:06:41 +02:00
Hugo van Kemenade 34690f68cc
Merge pull request #336 from kvanzuijlen/master 2020-12-30 12:55:30 +02:00
Koen van Zuijlen c851b82a1d Reverted temporary files change 2020-12-29 22:12:43 +01:00
Koen van Zuijlen b992d26138 Bugfix for creation of temporary files 2020-12-29 21:19:46 +01:00
Koen van Zuijlen 421c80a617 Merge branch 'streaming' 2020-12-29 20:59:29 +01:00
Hugo van Kemenade 10107a04e4 Merge remote-tracking branch 'upstream/master' into streaming 2020-12-29 21:38:44 +02:00
Hugo van Kemenade 8be8c4efb6 No need to set param with default 2020-12-29 21:32:29 +02:00
Hugo van Kemenade 0999501600 Fix comment 2020-12-29 21:24:05 +02:00
Hugo van Kemenade 66b4cb7d10
Merge pull request #355 from pylast/fix-default-limit 2020-12-28 23:30:39 +02:00
Hugo van Kemenade 7327b30337 Refactor to avoid shadowing built-in 2020-12-27 14:22:41 +02:00
Hugo van Kemenade 23503a7212 Refactor to remove unused parameter 2020-12-27 14:22:20 +02:00
Hugo van Kemenade a5034c43c0 Refactor with pytest.mark.parametrize 2020-12-27 14:05:52 +02:00
Hugo van Kemenade 08274028eb Set limit to 50 by default, not 1 2020-12-27 14:01:12 +02:00
Koen van Zuijlen 7193eb0d1f Merge remote-tracking branch 'pylast/master' 2020-12-24 20:43:06 +01:00
Hugo van Kemenade 46a3dbf4a6
Merge pull request #352 from pylast/rm-travis 2020-11-17 18:14:37 +02:00
Hugo van Kemenade 9033debfcd Replace Travis CI with GitHub Actions 2020-11-17 17:58:43 +02:00
Hugo van Kemenade e4ca881c95 pre-commit autoupdate 2020-11-17 17:50:28 +02:00
Hugo van Kemenade 0ba17ecfff Deploy to TestPyPI on merges to master, to prod PyPI for tags 2020-11-17 17:46:50 +02:00
Hugo van Kemenade e98f493c39 Use version resolver with Release Drafter 2020-11-17 17:40:56 +02:00
Hugo van Kemenade c979b72cf3 Label sync: don't delete existing labels not found in manifest 2020-11-17 17:39:48 +02:00
Hugo van Kemenade 5413d636ce Fix test 2020-11-16 23:45:25 +02:00
Hugo van Kemenade 815dc62dcd Test on GitHub Actions 2020-11-16 23:28:03 +02:00
Hugo van Kemenade b7e2cce725 Remove Travis CI 2020-11-16 22:41:44 +02:00
Hugo van Kemenade d7e1d70c34 Use pre-commit/action 2020-11-16 22:41:24 +02:00
Hugo van Kemenade 69d5d6ec9a
Merge pull request #350 from sheetalsingala/travisCI
Add Python 3.9 final to Travis CI
2020-10-28 22:46:47 +02:00
sheetalsingala c218aab4dd Add Python 3.9 final to Travis CI 2020-10-28 12:42:46 -04:00
Hugo van Kemenade b6eb1c8baf
Add Hacktoberfest labels 2020-10-28 16:17:15 +02:00
Hugo van Kemenade 49e2831cf6 Code formatting 2020-10-07 21:27:17 +03:00
Hugo van Kemenade b5a9617cdf 3.9-dev first 2020-10-07 21:26:42 +03:00
Hugo van Kemenade e888739b14 Add 4.0.0 to changelog 2020-10-07 21:02:08 +03:00
Hugo van Kemenade 6da916e78d tox-ini-fmt 2020-10-07 20:55:58 +03:00
mergify[bot] 3a4ad741bd
Merge pull request #348 from pylast/rm-deprecations
Remove deprecated Artist.get_cover_image, User.get_artist_tracks and STATUS_TOKEN_ERROR
2020-09-11 21:35:42 +00:00
mergify[bot] c0298212be
Merge pull request #347 from pylast/add-3.9
Add support for Python 3.9
2020-09-11 21:24:46 +00:00
Hugo van Kemenade 85f58472a3 Remove deprecated Artist.get_cover_image, User.get_artist_tracks and STATUS_TOKEN_ERROR 2020-09-12 00:21:12 +03:00
Hugo van Kemenade bf753884c4 Add support for Python 3.9 2020-09-12 00:13:19 +03:00
Hugo van Kemenade 91f79fd4b0
Merge pull request #346 from pylast/updates 2020-09-12 00:10:51 +03:00
Hugo van Kemenade 08ff008505 Drop support for EOL Python 3.5 2020-09-11 23:56:15 +03:00
Hugo van Kemenade 0f96fe58b1 Format with Black and isort 2020-09-11 23:47:42 +03:00
Hugo van Kemenade 2d570b97ff Update config 2020-09-11 23:44:57 +03:00
Hugo van Kemenade 99e0cc734a Remove W503 from Flake8 ignore list (ignored by default) 2020-08-31 23:03:10 +03:00
mergify[bot] f8a6c20ade
Merge pull request #343 from pylast/run-xfail-only-once
Run xfail tests only once: no point re-running
2020-08-23 15:09:55 +00:00
Hugo 3129d6052d Run xfail tests only once: no point re-running 2020-08-23 12:11:07 +03:00
mergify[bot] 77c17a9c36
Merge pull request #342 from pylast/cover-tests
Include tests in coverage
2020-08-22 09:09:49 +00:00
Hugo 48f4be0bcf Rewrite and add pragmas to improve coverage of non-runnable test code 2020-08-22 11:45:33 +03:00
Hugo 66f5ace917 pre-commit autoupdate 2020-08-22 10:38:42 +03:00
Hugo 574476e44c Include tests in coverage https://nedbatchelder.com/blog/202008/you_should_include_your_tests_in_coverage.html 2020-08-22 10:36:22 +03:00
Koen van Zuijlen 136b7f1cef Made stream=False the default instead of stream=True 2020-07-14 03:42:44 +02:00
kvanzuijlen a769b611f0
Added -e argument for editable installs and added quotes for some shells
Co-authored-by: Hugo van Kemenade <hugovk@users.noreply.github.com>
2020-07-14 03:17:27 +02:00
Koen van Zuijlen 0aa6de4769 Merge remote-tracking branch 'pylast/master' 2020-07-13 01:16:49 +02:00
Hugo van Kemenade 041eeeba80
Merge pull request #337 from pylast/add-mergify-label
Add automerge label for Mergify
2020-07-12 14:38:30 +03:00
kvanzuijlen 52abbba2bd tox lint changes 2020-07-12 13:32:32 +02:00
kvanzuijlen 15672922a7 General code improvements 2020-07-12 13:32:24 +02:00
kvanzuijlen 99fb7cd7a5 Added a parameter to choose whether to include now playing or not 2020-07-12 13:32:13 +02:00
kvanzuijlen 11d955dd89 Now playing shouldn't count as a recently played track 2020-07-12 13:32:01 +02:00
kvanzuijlen 92004058ba Added option to stream from resources to reduce memory usage 2020-07-12 13:31:48 +02:00
Hugo c2da9a6365 Add automerge label for Mergify 2020-07-12 14:19:22 +03:00
mergify[bot] 705052ae66
Merge pull request #334 from pylast/travis-test-dev
Test Python 3.9-dev and 3.10-dev on Travis CI
2020-06-25 18:28:16 +00:00
Hugo van Kemenade 7f59ced4dc
Merge pull request #335 from pylast/mergify/hugovk/config-update 2020-06-25 21:27:39 +03:00
Hugo van Kemenade 43971c05fd
Also require GHA lint
The checks show up like this:

Lint / build (pull_request)
Lint / build (push)

And the condition needs to match the job name, "build".

https://doc.mergify.io/conditions.html#github-actions

TODO: rename "build"->"lint" in lint.yml
2020-06-25 21:09:46 +03:00
Hugo van Kemenade 7c42504d68 Mergify: configuration update 2020-06-25 21:05:40 +03:00
Hugo 0300b52b4b Test Python 3.9-dev and 3.10-dev on Travis CI 2020-06-25 20:35:19 +03:00
Hugo van Kemenade 1844c65f81
Check importable and version is updated 2020-06-25 19:24:29 +03:00
Hugo van Kemenade 00f7014c51
Add 3.3.0 to changelog 2020-06-25 19:13:46 +03:00
Hugo van Kemenade 6f2bdb5db8
Merge pull request #333 from pylast/isolate-write-tests
Only test write operations on a single Python version to avoid collision failures
2020-06-25 08:27:01 +03:00
Hugo 32461c089d Only test write operations on a single Python version to avoid collision failures 2020-06-24 22:58:25 +03:00
Hugo van Kemenade e7ad4ac5b2
Merge pull request #332 from pylast/deprecate-artist.get_cover_image 2020-06-24 09:47:51 +03:00
Hugo e188e78bdd pre-commit autoupdate, show diff on failure, and force colour on GHA 2020-06-23 11:21:40 +03:00
Hugo 158273e0ae Deprecate Artist.get_cover_image, they're no longer available from Last.fm 2020-06-22 22:20:45 +03:00
Hugo van Kemenade 108e3dda44
Merge pull request #330 from pylast/add-album-and-image-to-get_now_playing 2020-06-22 21:50:22 +03:00
Hugo 6f62857164 Add test for get_album() changes 2020-06-02 16:02:26 +03:00
Hugo d467e2ceb7 If we already have the album, return early 2020-06-01 21:45:11 +03:00
Hugo aae4bb3693 Replace unittest with pytest 2020-06-01 15:50:27 +03:00
Hugo 1160ee1513 GHA: Simplify and update lint.yml 2020-06-01 15:20:36 +03:00
Hugo 8437316d3d pre-commit autoupdate 2020-06-01 14:57:33 +03:00
Hugo 7689a1e95a User.get_now_playing: Add album and cover image to info 2020-06-01 14:52:29 +03:00
Hugo van Kemenade 898a8b5756
Merge pull request #326 from pylast/fix-if-bio-is-empty
Fix artist.get_bio_content() to return None if bio is empty
2020-05-10 10:14:46 +03:00
Hugo 925cae2031 Add test for artist with no bio content 2020-05-08 12:42:36 +03:00
Hugo 26f2173160 pre-commit autoupdate 2020-05-08 12:42:36 +03:00
Hugo a373654500 Return None if bio section is empty 2020-05-08 12:38:02 +03:00
Hugo van Kemenade 2aaa594388
Merge pull request #327 from spiritualized/master 2020-05-08 12:24:12 +03:00
spiritualized 6a28ba076f Improve handling of error responses from the API 2020-05-06 22:43:16 -04:00
Hugo a040f42f95 Use v2 of actions/checkout 2020-03-23 14:29:00 +02:00
Hugo van Kemenade b4092695a0
Add Zenodo DOI badge 2020-03-20 19:05:55 +02:00
Hugo van Kemenade 5391e0d233
Create FUNDING.yml 2020-03-13 17:15:02 +02:00
Hugo fa0189d9bc Update release checklist for Release Drafter 2020-03-05 00:52:13 +02:00
Hugo van Kemenade dfb694f2a6
Add 3.2.1 2020-03-05 00:46:04 +02:00
Hugo van Kemenade 9622fca501
Merge pull request #323 from pylast/fix-deprecation-warning
Fix DeprecationWarning: Please use assertRegex instead
2020-03-04 22:57:54 +02:00
Hugo f806a1f36a Fix DeprecationWarning: Please use assertRegex instead 2020-03-04 22:37:05 +02:00
Hugo van Kemenade 0f80585d72
Merge pull request #322 from pylast/pre-commit-cache
GHA: Fix lint cache
2020-03-04 22:36:47 +02:00
Hugo 7005dbc13f Bump to test with a new cache 2020-03-04 22:22:33 +02:00
Hugo c8e3162b9d Add a version number to cache file to invalidate it 2020-03-04 22:18:39 +02:00
Hugo a0255a61e1 Add a version number to cache file to invalidate it 2020-03-04 22:16:50 +02:00
Hugo van Kemenade d367d913c2
Merge pull request #320 from pylast/313-limit-none
Fix regression calling get_recent_tracks with limit=None
2020-03-04 21:57:46 +02:00
Hugo van Kemenade 2e90f1e424
Merge pull request #321 from pylast/release-drafter-and-label-syncer
Use Release Drafter to draft releases
2020-02-27 22:50:02 +02:00
Hugo af036ec5d5 pre-commit autoupdate 2020-02-27 22:29:32 +02:00
Hugo 4e98e7af4c Use release drafter to draft releases 2020-02-27 22:23:16 +02:00
Hugo van Kemenade e22ad6b464
Link to CHANGELOG.md
[CI skip]
2020-02-15 18:34:20 +02:00
Hugo 21f127f88b Fix regression calling get_recent_tracks with limit=None 2020-02-15 18:19:59 +02:00
Hugo van Kemenade ac5fb41c68
Merge pull request #318 from pylast/rm-universal
Only Python 3 is supported: don't create universal wheel
2020-02-04 12:10:04 +02:00
Hugo cd18581fe2 Only Python 3 is supported: don't create universal wheel 2020-02-04 11:57:40 +02:00
Hugo 27228e785f Update instructions: include changelog in release 2020-01-03 13:43:16 +02:00
Hugo 662e3bbddd Update copyright year 2020-01-03 13:26:27 +02:00
Hugo ab4fa70fe9 Update changelog for pylast 3.2.0 2020-01-03 13:26:15 +02:00
Hugo f9ced38512 Add known_third_party to isort and pre-commit 2020-01-03 13:23:01 +02:00
Hugo van Kemenade 7f7d179793
Use setuptools_scm to simplify versioning during release (#316)
Use setuptools_scm to simplify versioning during release
2019-12-27 23:35:18 +02:00
Hugo ad2f9cbaa2 Use setuptools_scm to simplify versioning during release 2019-12-27 23:13:58 +02:00
Hugo van Kemenade 364f72f827
Lint with GitHub Actions (#315)
Lint with GitHub Actions
2019-12-27 22:17:34 +02:00
Hugo 846ca20895 Add flake8-implicit-str-concat to pre-commit 2019-12-27 22:08:29 +02:00
Hugo baf808a465 Lint with GitHub Actions 2019-12-22 21:00:47 +02:00
Hugo van Kemenade 7d5947341b
Test updates (#314)
Test updates
2019-12-20 23:43:35 +02:00
Hugo 13bccb37b6 Remove outdated INSTALL file, instead: pip install pylast 2019-12-20 23:29:40 +02:00
Hugo 0fd17e6a6e Add downloads badge 2019-12-18 15:40:41 +02:00
Hugo 7b5a365074 Remove Scrutinizer 2019-12-17 23:35:53 +02:00
Hugo 4329080457 Cache pre-commit, use tox-travis to simplify config, only coverage for non-lint 2019-12-17 22:55:33 +02:00
Hugo ec43b92c27 Use extras_require for tox deps 2019-12-17 22:48:05 +02:00
Hugo 3673c07994 Concatenate strings, post-Black 2019-12-17 22:47:39 +02:00
Hugo 6b04456c55 Mock is part of stdlib in Python 3 2019-12-17 22:32:53 +02:00
Hugo e8429b07bc Don't pin to a Python version 2019-12-17 22:23:16 +02:00
Hugo d28c0ec167 Xfail those two to allow the rest to pass 2019-12-17 22:19:57 +02:00
Hugo 44e47d84ce Add support for Python 3.8 2019-12-17 22:17:17 +02:00
Hugo van Kemenade 782040dc28
Merge pull request #312 from pylast/rm-py2-warnings
Remove Python 2 warnings, apply lint updates
2019-12-14 14:23:19 +02:00
Hugo 109315d93d Remove Python 2 warnings, apply lint updates 2019-09-26 11:10:52 +03:00
Hugo van Kemenade 7383fa20cf
Update get_weekly_artist_charts docstring (#311)
Update get_weekly_artist_charts docstring
2019-09-26 11:01:14 +03:00
Hugo 99c434c0ef Run lint on Python 3.7 2019-09-26 10:36:16 +03:00
Hugo 3fd8280e33 Update docstring: since 2016 get_weekly_artist_charts is only for users, not tags 2019-09-26 10:34:22 +03:00
Hugo d589c5ef8a Add pre-commit and fix isort 2019-09-26 10:31:49 +03:00
Hugo van Kemenade 695a11a4cd
Respect get_recent_tracks' limit when there's a now playing track (#310)
Respect get_recent_tracks' limit when there's a now playing track
2019-09-20 22:47:35 +03:00
Hugo 7915d7f3e6 Respect get_recent_tracks' limit when there's a now playing track 2019-09-19 14:27:20 +03:00
Hugo fc51315f48
Store Album Art URLs when you call GetTopAlbums. (#307)
Store Album Art URLs when you call GetTopAlbums.
2019-06-24 09:05:15 +03:00
Paddez 7694de346e Store Album Art URLs when you call GetTopAlbums.
GetTopAlbums returns AlbumArt information alongside the TopAlbums.
We should store this in the Album() info variable, so we don't have to follow up with an additional GetInfo() call if we want the Album Art.

Signed-off-by: Paddez <dave@paddez.com>
2019-06-22 22:36:41 +01:00
Hugo 46e4c0ec4c
Update user.get_artist_tracks tests (#305)
Update user.get_artist_tracks tests
2019-06-03 21:56:33 +03:00
Hugo c7b927d94c user.getArtistTracks has been removed from Last.fm 2019-06-03 16:45:54 +03:00
Hugo 44d0c2c966 user.getArtistTracks has now been removed from Last.fm and returns an error 2019-06-02 22:54:03 +03:00
Hugo dedcbf00f5
Remove deprecated license_file from setup.cfg (#303)
Remove deprecated license_file from setup.cfg
2019-05-13 11:21:39 +03:00
Hugo f439f205dc Xfail user.getArtistTracks, looks like Last.fm are now removing it from the API 2019-05-13 11:08:18 +03:00
Hugo 9ec4e99271 Update Black link 2019-05-10 09:03:03 +03:00
Hugo ce9ab4213e Remove deprecated license_file from setup.cfg 2019-05-10 09:02:53 +03:00
Hugo 05a7fc07f1 Black is now under the PSF umbrella https://twitter.com/llanga/status/1123980466292445190 2019-05-04 20:57:05 +03:00
Hugo a52f66d316
Merge pull request #301 from pylast/mv-to-src
Move installable code to src/
2019-04-14 19:10:27 +03:00
Hugo ab77165ecc Revert "Don't fail due to Last.fm bug"
This reverts commit c3b36433b2.
2019-04-14 18:35:10 +03:00
Hugo b9ef29fa21 Simplify via https://blog.ionelmc.ro/2014/05/25/python-packaging/#the-structure 2019-04-05 10:48:58 +03:00
Hugo c3b36433b2 Don't fail due to Last.fm bug 2019-04-05 09:00:31 +03:00
Hugo 1a78c0b99d Move production code to src/ 2019-04-05 08:37:01 +03:00
Hugo df6e31b4ff
Merge pull request #297 from pylast/retry-fetch
Retry paging through results on exception
2019-04-04 22:54:46 +03:00
hugovk a5c53dbf54 Start new release cycle 2019-03-07 09:44:45 +02:00
hugovk 694f67b25a Release 3.1.0 2019-03-07 09:27:32 +02:00
Hugo c95e39b624
Merge pull request #299 from pylast/deprecate-User.get_artist_tracks
Deprecate User.get_artist_tracks, add user.getTrackScrobbles
2019-03-07 09:07:59 +02:00
Hugo 2284dcf580 Update docs 2019-02-27 19:56:29 +02:00
Hugo da473448f4 Add support for new user.getTrackScrobbles 2019-02-27 19:47:31 +02:00
Hugo dc464788f2 Aside: make test_album_tags_are_topitems more reliable 2019-02-27 17:19:20 +02:00
Hugo 65e182c5dd Aside: make test_scrobble more reliable 2019-02-27 17:01:23 +02:00
Hugo 9d125506e5 Deprecate User.get_artist_tracks as Last.fm will remove it soon 2019-02-26 22:53:31 +02:00
Hugo 86c979204d year++ 2019-02-10 17:30:04 +02:00
Hugo 9b2ada5dd0 Retry on any exception
Also seen these when testing:

WSError: User not found
MalformedResponseError: Malformed response from Last.fm. Underlying error: mismatched tag: line 6, column 2
NetworkError: NetworkError: [Errno 8] nodename nor servname provided, or not known
2019-02-10 16:06:58 +02:00
Hugo f32848160c Make test more reliable: check a static album not a dynamic one which may not have any tags 2019-02-10 11:58:40 +02:00
Hugo bfd3ffe06c Retry on temporary error when paging 2019-02-10 11:31:42 +02:00
Hugo e38d1c516b Update error status codes from https://www.last.fm/api/errorcodes 2019-02-09 23:18:51 +02:00
Hugo 69b54e8c45 Update encrypted password 2019-02-03 20:56:42 +02:00
Hugo 29f38f9531
Merge pull request #295 from pylast/deploy-to-pypi-on-tag
Deploy to PyPI on master
2019-02-03 20:28:55 +02:00
Hugo 5406261679 In case lint is done on 'python: 3.7', deploy on a TOXENV 2019-02-03 15:08:35 +02:00
Hugo deb25fb030 Deploy to PyPI on master 2019-02-03 15:08:35 +02:00
Hugo 6a1a5a450e
Merge pull request #294 from pylast/fix-test
Fix the build
2019-02-03 15:07:05 +02:00
Hugo b41c5bc85a Add a delay to allow Last.fm to update unloves 2019-02-03 14:39:21 +02:00
Hugo d9c086f8cb
Merge pull request #293 from pylast/fix-test
Fix the build
2019-02-03 14:23:20 +02:00
Hugo 0ad27dbc7e Only run test_unlove on a single Python version to avoid collisions 2019-02-03 14:02:28 +02:00
Hugo 7f207c50da Add a delay to allow Last.fm to update unloves 2019-02-03 13:55:54 +02:00
Hugo d7750d8131
Merge pull request #292 from pylast/shuffle-test-order
Fix the build
2019-02-03 13:38:40 +02:00
Hugo 4eec2e763d Only run test_love on a single Python version to avoid collisions 2019-02-03 12:51:44 +02:00
Hugo d4fe9e5b36 Add a delay to allow Last.fm to update scrobbles 2019-02-03 12:42:50 +02:00
Hugo cfb8134872 Only run test_scrobble on a single Python version to avoid collisions 2019-02-02 14:15:31 +02:00
Hugo 850d746b60 Test in random order to prevent collisions 2019-02-02 13:54:18 +02:00
Hugo 45a1b7c7f1 Test in random order to prevent collisions 2019-02-02 13:40:17 +02:00
Hugo b30fe33cc1 The future is now 2019-01-27 14:42:46 +02:00
Hugo 71e9871b1b
sudo no longer needed
https://blog.travis-ci.com/2018-11-19-required-linux-infrastructure-migration
2019-01-09 16:19:22 +02:00
Hugo 1a31e4e0c3 Add #290 to changelog 2019-01-06 15:39:12 +02:00
Hugo van Kemenade d121f2c9cf
Merge pull request #290 from jacebrowning/fix-blank-username
Extract username from session
2019-01-06 15:33:20 +02:00
Jace Browning fffc647811 Shorten line length 2019-01-05 20:15:49 -05:00
Jace Browning 0a38ac801d Ignore line length rules in favor of black formatting 2019-01-05 16:28:12 -05:00
Jace Browning aa8adfee4b Create a separate method for extracting username 2019-01-05 16:19:47 -05:00
Jace Browning e77e0f9e35 Extract username from session 2019-01-05 14:32:36 -05:00
Hugo 6134945df7 Update release checklist 2019-01-02 14:43:52 +02:00
Hugo 9968d5f06e Start new release cycle 2019-01-01 18:05:12 +02:00
Hugo f67afa0d58 Update release checklist 2019-01-01 18:01:42 +02:00
Hugo eb53483f6f Release 3.0.0 2019-01-01 17:13:16 +02:00
Hugo c9fa096ef2 No need for .git extension 2018-12-30 13:13:49 +02:00
Hugo 4626ff9670 Reorder setup params 2018-12-30 13:13:49 +02:00
Hugo 718fba04b8
Merge pull request #289 from pylast/rm-2-3.4
Drop support for legacy Python 3.4
2018-12-30 00:18:54 +02:00
hugovk ee879aa042 Upgrade Python syntax with pyupgrade --py3-plus 2018-12-30 00:08:30 +02:00
hugovk 742df8b674 Drop support for Python 3.4 (EOL 2019-03-16) 2018-12-30 00:08:30 +02:00
Hugo 53806b0a71
Merge pull request #282 from pylast/rm-deprecated-constants
Remove deprecated COVER_X constants
2018-12-30 00:02:23 +02:00
Hugo 7707c3ced4
Merge branch 'master' into rm-deprecated-constants 2018-12-29 23:47:16 +02:00
Hugo d63c0db472
Merge pull request #281 from pylast/rm-2
Drop support for legacy Python 2
2018-12-29 23:44:36 +02:00
Hugo 07fac0628c Drop support for legacy Python 2 2018-11-27 08:59:01 +02:00
Hugo 5fded8398b
Merge pull request #288 from hugovk/add-3.8-dev
CI: Test on 3.8-dev, remove 3.6-dev and 3.7-dev
2018-11-27 00:09:00 +02:00
hugovk 51821e4a9a Xenial required for 3.7+ 2018-11-26 23:25:58 +02:00
hugovk f9a130f1ba Test on 3.8-dev, remove outdated 3.6-dev and 3.7-dev 2018-11-26 23:18:17 +02:00
Hugo 08294f0c52
Merge pull request #285 from pylast/ci-vm
Use VM-based infrastructure
2018-11-19 23:00:11 +02:00
Hugo 48f1d72c45
Use VM-based infrastructure
More info:

https://blog.travis-ci.com/2018-11-19-required-linux-infrastructure-migration
2018-11-19 22:41:41 +02:00
Hugo eeb65d9724 Update changelog and version 2018-08-14 16:37:39 +03:00
Hugo d1d17314b2 Remove deprecated COVER_X constants 2018-08-14 16:14:37 +03:00
Hugo 21ccdeb9fb
Merge pull request #280 from pylast/add-changelog
Add a changelog
2018-08-14 16:00:30 +03:00
Hugo c269f3567c
Add a changelog
[CI skip]
2018-08-09 11:36:21 +03:00
Hugo 245dd47a41 Start new release cycle 2018-08-08 11:23:04 +03:00
Hugo bf68d17ca2 Release 2.4.0 2018-08-08 11:03:59 +03:00
Hugo 51190559d9
Merge pull request #279 from pylast/add-3.7
Test on official Python 3.7
2018-07-29 23:12:37 +03:00
Hugo 518890423a Test on official Python 3.7 2018-07-29 12:57:34 +03:00
Hugo cd8d29ab91
Merge pull request #277 from pylast/deprecate-py2
Deprecate Python 2
2018-07-27 16:20:38 +03:00
Hugo be52e1793a Black fix 2018-07-27 16:02:00 +03:00
Hugo c1e70f798c UserWarning when importing on Python <3 https://python3statement.org/practicalities/#runtime-warning-on-master 2018-07-27 15:36:59 +03:00
Hugo 905d06ef5d Upgrade Python syntax with pyupgrade 2018-07-27 15:35:38 +03:00
Hugo 0b725831d2
Merge pull request #275 from pylast/rm-pyyaml-workaround
Revert PyYAML workaround for Python 3.7
2018-06-27 16:50:05 +03:00
Hugo 7665fd09d7 Revert "Add workaround for build failures on Python 3.7 (https://github.com/yaml/pyyaml/issues/126)"
This reverts commit f2ec5bc57e.
2018-06-27 16:30:29 +03:00
Hugo 691e0a9ab1
pylast >= 3.0.0 will likely only support Python 3.4+
See https://github.com/pylast/pylast/issues/265.
2018-06-23 22:57:08 +03:00
Hugo 1997154a81
Merge pull request #272 from pylast/single-source-package-version
Single-source the package version
2018-06-09 10:17:36 +03:00
Hugo 907891bcb1 Include version file in manifest 2018-06-09 09:50:08 +03:00
Hugo ca11de0a3a Black fix 2018-06-09 09:44:59 +03:00
Hugo d16423c343 CI: Also show Black diff on failure 2018-06-09 09:44:39 +03:00
Hugo efd123004a Update release docs 2018-06-09 09:43:41 +03:00
Hugo c6b080d578 Single-source the package version 2018-06-09 09:40:15 +03:00
Hugo 35b97356d1 Single-source the package version 2018-06-08 17:53:13 +03:00
Hugo 5d085ba9dc Start new release cycle 2018-06-08 17:41:40 +03:00
Hugo 4c89c7bf5a Release 2.3.0 2018-06-08 17:37:26 +03:00
Hugo 5c0785f7fe
Merge pull request #271 from pylast/pyupgrade
Run pyupgrade
2018-06-08 17:34:43 +03:00
Hugo d65e2bdb3e Run Black 2018-06-08 15:28:49 +03:00
Hugo 018675aedd Run pyupgrade 2018-06-08 15:28:25 +03:00
Hugo cfaef14a94
Merge pull request #270 from pylast/run-black
Format code with Black
2018-06-06 11:55:43 +03:00
Hugo 5a51d52e0a
Add Black badge 2018-06-06 11:37:40 +03:00
Hugo 6503002692 Fix commented-out formatting post-Black 2018-06-06 11:26:00 +03:00
Hugo 9194232ecd Sort tests_require 2018-06-06 11:23:39 +03:00
Hugo ba946170da Add 'max_line_length = 88' for flake8 2018-06-06 10:39:02 +03:00
Hugo da9465c100 Format with Black 18.6b1 2018-06-06 10:19:04 +03:00
Hugo e5a5278d51 Check Black on CI 2018-06-06 10:14:17 +03:00
Hugo f3c9b4150d Use Black's default 88 line length 2018-06-06 10:14:17 +03:00
Hugo 8bca1d2794 Keep flaky max_runs=3, sometimes build jobs overlap a bit 2018-05-27 20:58:33 +03:00
Hugo c8d349cfb6
Merge pull request #269 from pylast/fix-ci
Fix some tests and code to make tests pass
2018-05-27 20:44:26 +03:00
Hugo 000993c328 Fix test_cacheable_user[_artist_tracks] 2018-05-27 20:14:13 +03:00
Hugo 4aefda5d39 A user without a country can now have '<country>None</country>' 2018-05-27 19:28:59 +03:00
Hugo b3a4ac5388 Fix: 'list' object has no attribute 'track' 2018-05-27 19:23:54 +03:00
Hugo 30d512fd0e Fix pep8-naming 2018-05-20 18:41:27 +03:00
Hugo 2194245e75
Merge pull request #268 from pylast/skip-artist-in-tracks
'Tracks' with only one <name> are artists not tracks, skip them
2018-05-16 12:03:18 +03:00
Hugo eae4b6261c Add '# pragma: no cover' as no easy way to test 2018-05-16 11:45:44 +03:00
Hugo fce5e5d830 'Tracks' with only one <name> are artists not tracks, skip them 2018-05-11 22:07:28 +03:00
Hugo 61e95cc2a4
Merge pull request #267 from pylast/pep8-naming
Add pep8-naming to flake8 and fix issues
2018-05-11 17:08:53 +03:00
Hugo 1d5e965183 Fix pep8-naming 2018-05-11 10:39:16 +03:00
Hugo 7ac0cbb898 Add pep8-naming to flake8 linting 2018-05-11 10:31:24 +03:00
Hugo d35daaaeba Four-space indents 2018-05-11 10:24:28 +03:00
Hugo e3537d5b86 Switch lint to flake8 2018-05-11 10:22:58 +03:00
Hugo fad76315bb
Merge pull request #264 from pylast/save-artist-images
Save artist images and refactor album images
2018-04-18 12:09:08 +03:00
Hugo 8def14de48 Tests no longer flaky, let's keep but reduce in case of temp net errors 2018-04-16 21:57:29 +03:00
Hugo f18fd3f758 TestPyLastWithLastFm and TestPyLastWithLibreFm now inherit from PyLastTestCase 2018-04-16 21:09:52 +03:00
Hugo 70aad87a1b Store track images for future use, refactor album image fetching 2018-04-16 19:02:32 +03:00
Hugo 60dea38d10 Add assert_startswith/endswith helper functions 2018-04-16 18:31:33 +03:00
Hugo fa601b8111 Refactor to store Album/Artist images in info dict 2018-04-16 09:53:31 +03:00
Hugo d1af8d3ebc Store artist images for future use 2018-04-15 23:43:39 +03:00
Hugo 942ced319a Capitalise URI/URL in comments 2018-04-15 23:32:23 +03:00
Hugo 769d70c00b
Merge pull request #263 from pylast/logging
Replace print with logging
2018-04-15 22:10:27 +03:00
Hugo f923cd2c50 Replace print with logging 2018-04-15 21:55:08 +03:00
Hugo cef02d0700
Merge pull request #262 from zinootje/master
Save image URLs to Album on Album.get_cover_image and AlbumSearch.get_next_page
2018-04-15 17:21:36 +03:00
zinootje c3476c1133
Merge pull request #1 from pylast/zinootje-master
Store all images on get_cover_image for future use
2018-04-15 11:46:06 +02:00
Hugo 4083f72ef7 Store all images on get_cover_image for future use 2018-04-14 22:50:42 +03:00
Hugo cf2d9113dd Add test for storing album images on search 2018-04-14 22:38:43 +03:00
Hugo 971e4e3571 flake8 2018-04-14 22:06:57 +03:00
zinootje c2dfe04e17 add images url on get next page 2018-04-14 19:43:47 +02:00
Hugo a144ce407d
Merge pull request #260 from hugovk/deprecation-warnings
Close HTTPSConnection in case of exception
2018-04-11 16:38:47 +03:00
Hugo 887a47c515 Ignore new '.pytest_cache' directory 2018-04-11 11:25:42 +03:00
Hugo 85efc31c10 Close HTTPSConnection in case of exception 2018-04-11 10:38:00 +03:00
hugovk c929b5bd8f Show deprecation warnings for tests 2018-04-11 00:19:24 +03:00
Hugo 1181dc4f35
Link badges to all new PyPI
[CI skip]
2018-04-08 17:23:18 +03:00
Hugo 4cf7e8c727 Start new release cycle 2018-04-08 17:18:10 +03:00
Hugo 2e52023a22 Release 2.2.0 2018-04-08 17:12:36 +03:00
Hugo a607dbe491 flake8 2018-04-08 16:45:36 +03:00
Hugo 8b4866682d
Merge pull request #259 from pylast/use-markdown-on-pypi
Use Markdown on PyPI
2018-04-06 14:11:13 +03:00
Hugo c5684bb87a Use Markdown on PyPI 2018-04-06 13:11:28 +03:00
Hugo 72cdd8b979
Merge pull request #258 from pylast/fix-user.get_weekly_artist_charts
Fix user.get_weekly_artist_charts
2018-04-05 18:28:17 +03:00
Hugo dfb1ce2e2e
pylast 2.2.0 will support Python 3.7
[CI skip]
2018-04-05 18:10:59 +03:00
Hugo 892acd6df9 Artist charts have the artist name in the 'name' field. There is no 'artist' field. 2018-04-05 18:08:21 +03:00
Hugo 06f6cf627d Failing test for user.get_weekly_artist_charts 2018-04-05 18:02:16 +03:00
Hugo 68ebcf9487
Merge pull request #255 from pylast/python3.7
Add Python 3.7
2018-03-27 16:40:13 +03:00
Hugo 8c5799a4d6 Refactor lint to remove duplicate 2018-03-27 15:52:16 +03:00
Hugo f2ec5bc57e Add workaround for build failures on Python 3.7 (https://github.com/yaml/pyyaml/issues/126) 2018-03-27 15:52:16 +03:00
Hugo 20250237b9 Add Python 3.7 2018-03-26 19:41:54 +03:00
Hugo ea87739458
Merge pull request #254 from pylast/improve-test
Test against a certain user
2018-03-18 22:43:43 +02:00
Hugo 0759bf5b0c
Test against a certain user
One we know has more than two top tracks, rather than a new test user who might not.
2018-03-18 09:58:41 +02:00
Hugo aba895d56f
Merge pull request #252 from pylast/fix-mutable-default-argument
Do not use a mutable data structure for argument default
2018-03-15 22:10:04 +02:00
Hugo 8670a87255 Do not use a mutable data structure for argument default 2018-03-15 21:52:41 +02:00
Hugo 77e06add28 Add 'and Contributors' to Author 2018-03-15 10:27:51 +02:00
Hugo 0a152fa35d Codecov: Avoid "Missing base report" [CI skip] 2018-03-01 11:47:03 +02:00
Hugo c882d8dea3
Merge pull request #251 from pylast/fix-tests
Fix test case for changed test data on Last.fm
2018-03-01 11:42:00 +02:00
Hugo acdc23d7e7 Fix test case for changed test data 2018-03-01 11:24:53 +02:00
Hugo b87fefe9ab Move track tests to test_track.py 2018-03-01 11:23:06 +02:00
Hugo 36f7e9619b Shorten test names 2018-03-01 11:19:34 +02:00
Hugo 6fd84421bd
Merge pull request #250 from redshodan/get-similar-limit
Add missing limit parameter for track.getSimilar
2018-03-01 11:12:03 +02:00
Chris Newton 9cb6f6e181 Added a test case for limits on track.getSimilar 2018-02-28 20:28:56 -07:00
Chris Newton bb589a4d21 Add missing limit parameter for track.getSimilar 2018-02-28 17:04:46 -07:00
Hugo 612eb75080
Merge pull request #248 from pylast/add-templates
Add issue and PR templates
2018-02-08 14:25:52 +02:00
Hugo fa425188db Add issue and PR templates 2018-02-08 14:14:25 +02:00
Hugo adea26ec4c
Merge pull request #247 from pylast/use-named-version-attributes
Simplify version checking
2018-02-08 14:04:26 +02:00
Hugo 8e478e79ea Treat Py2 (not Py3) as the special case https://astrofrog.github.io/blog/2016/01/12/stop-writing-python-4-incompatible-code/ 2018-02-06 17:47:39 +02:00
Hugo 41f8ae95d1 Use named version attributes (new in Py2.7) 2018-02-06 17:46:08 +02:00
Hugo b02b830282 Remove Landscape badge, it's failing to check 2018-01-27 13:34:54 +02:00
Hugo 99ad248e6d Ignore Clone Digger output 2018-01-27 13:34:01 +02:00
Hugo e0f52ac27d
Merge pull request #245 from pylast/update-unittest-asserts
Use more helpful assert methods
2018-01-27 13:16:22 +02:00
Hugo 4f4d204300 Define __ne__ in terms of == instead of __eq__ 2018-01-27 13:01:22 +02:00
Hugo 2703b6b385 Use more helpful asserts, remove redundant 2018-01-27 12:59:20 +02:00
Hugo d1ea097cd7 Start new release cycle 2018-01-14 20:05:48 +02:00
Hugo 2389f745e1 Release 2.1.0 2018-01-14 20:01:42 +02:00
Hugo 423db55bd0
Merge pull request #244 from pylast/fix-cache-key-lookup-performance
Fix serious cache key lookup performance problem
2018-01-11 12:27:52 +02:00
Mice Pápai d24100f2db Fix serious cache key lookup performance problem
Checking if a request is cached or not took ~6sec / item vs <0.01sec now, because __contains__ wasn't definied for the _ShelfCacheBackend class and it iterated over each cache key on every check
2018-01-11 12:14:10 +02:00
Hugo a1e10e75cd
Merge pull request #243 from pylast/242-limit-100
Use paging to get all 'limit' items for Artist.get_top_albums
2018-01-11 11:50:07 +02:00
Hugo 309b156fca Use paging to get all 'limit' items for Artist.get_top_albums (and others using _get_things) 2018-01-11 11:36:14 +02:00
Hugo 22e03705c4 Passing test for limit=1 or 50, failing test for 100 2018-01-11 10:37:01 +02:00
Hugo edeec178c5 Make checklist more copy/pastable [CI skip] 2018-01-04 17:04:47 +02:00
Hugo 348d2ce7ba Update year 2018-01-04 16:58:37 +02:00
Hugo 969cbeb36c
Merge pull request #240 from pylast/python_requires
Update python_requires to 2.7.10+
2017-12-09 23:06:24 +02:00
Hugo 9abb5884b7
Update python_requires to 2.7.10+
Update python_requires to specify the exact x.y.z 2.7.10 version required.

(From HTTPS changes in #178.)
2017-12-09 22:54:12 +02:00
Hugo 038ab141d3
develop -> master
[CI skip]
2017-12-08 21:37:39 +02:00
Hugo 139fdddb44
Merge pull request #237 from pylast/develop
Merge develop into master
2017-12-08 21:36:33 +02:00
Hugo 9da36d15d7 Fix case-sensitive tests 2017-12-08 20:48:15 +02:00
Hugo 8bfb70bf28
Merge pull request #238 from pylast/ditch-develop
Update release checklist to remove develop branch
2017-12-08 20:34:21 +02:00
Hugo d3f6e65fa5
Update release checklist to remove develop branch
[CI skip]
2017-12-08 20:33:56 +02:00
Hugo a9dfcc8c34
Merge pull request #236 from pylast/python_requires
Require Python 2.7 or 3.4+
2017-11-15 15:23:36 +02:00
hugovk 7621673452 Require Python 2.7 or 3.4+ 2017-11-15 15:10:31 +02:00
Hugo 339b96faaa
Merge pull request #235 from PhilipTrauner/develop
Return unix timestamp as int
2017-11-02 20:43:06 +02:00
Philip Trauner 8f515d8779 Fixed test in accordance to 36ed30cc2b 2017-11-02 19:24:45 +01:00
Philip Trauner 36ed30cc2b Return unix timestamp as int 2017-11-02 18:41:15 +01:00
Hugo 128afc7862
How to install latest development version
[CI skip]
2017-11-02 19:03:42 +02:00
Hugo d58a8ee61f
Merge pull request #234 from PhilipTrauner/develop
Added parameter to retrieve higher resolution user avatars
2017-11-02 18:41:50 +02:00
Philip Trauner 18708393af Added depreciation comment, harmonized image sizes 2017-11-02 17:04:24 +01:00
Philip Trauner 35f67a09cb Replaced deprecated COVER_ constants with SIZE_ 2017-11-02 16:31:41 +01:00
Philip Trauner 0d98c3590f Added parameter to retrieve higher resolution user avatars 2017-11-02 01:09:46 +01:00
Hugo 41a3b96a0e
Merge pull request #227 from pylast/good-citizen
Be a good citizen: test 3.6-dev
2017-10-29 00:53:01 +03:00
Hugo 825432b77f Update with thanks to https://hynek.me/articles/sharing-your-labor-of-love-pypi-quick-and-dirty/ 2017-10-29 00:48:56 +03:00
Hugo 121e8a27c1 Start new release cycle 2017-10-29 00:47:42 +03:00
Hugo ae35af7e3b Release 2.0.0 2017-10-29 00:42:50 +03:00
Hugo 6d3ef3c7bf
Merge pull request #233 from pylast/develop
Merge develop into master
2017-10-29 00:20:21 +03:00
Hugo 0d8812caca
Merge pull request #232 from pylast/py.test-to-pytest
pytest is recommended entry point
2017-10-28 10:28:31 +03:00
Hugo 79fa0c6f9d pytest is recommended entry point 2017-10-28 10:01:47 +03:00
Hugo ec647d181c
Update tests info
[CI skip]
2017-10-28 10:00:14 +03:00
Hugo dc5e0ce843
Add supported Python versions badge
[CI skip]
2017-10-28 09:56:12 +03:00
Hugo ad24b8910b Merge pull request #231 from pylast/fix-code-inspection
Fix some PyCharm code inspection things
2017-10-26 15:46:54 +03:00
hugovk b2f58fde63 Remove commented-out, broken downloads badge 2017-10-24 00:57:33 +03:00
hugovk c601d2f365 Fix minor things from code inspection 2017-10-24 00:54:34 +03:00
Hugo 3a7cc9c410 Merge pull request #230 from pylast/fix-search-total
Fix search.get_total_result_count()
2017-10-24 00:43:20 +03:00
hugovk cb1f760731 http -> https 2017-10-24 00:11:49 +03:00
hugovk 52636b6764 Work around Last.fm's 'Namespace prefix opensearch on totalResults is not defined' XML error 2017-10-24 00:04:05 +03:00
Hugo 485d69184c Merge pull request #229 from pylast/pep8-to-pycodestyle
pep8 is now pycodestyle
2017-10-22 23:30:37 +03:00
Hugo 68db2da2e9 pep8 is now pycodestyle https://github.com/PyCQA/pycodestyle/issues/466 2017-10-22 23:17:27 +03:00
Hugo 3607deae80 Merge pull request #228 from pylast/no-skipping-broken-last.fm
No more skipping broken Last.fm and remove more dead Last.fm stuff
2017-10-20 00:59:41 +03:00
hugovk b559495366 Assume no illegal XML and no more skipping broken Last.fm 2017-10-20 00:51:34 +03:00
hugovk b55b40c3fe Remove dead Last.fm album/track get ID 2017-10-20 00:45:17 +03:00
hugovk 666181df50 Last.fm API broken but allow either yyyy-mm-dd or Unix timestamp 2017-10-20 00:39:02 +03:00
hugovk 00b6c0a619 Remove dead Last.fm tag.getSimilar 2017-10-20 00:32:40 +03:00
hugovk c0a25fbabe Remove dead Last.fm artist band members 2017-10-20 00:29:57 +03:00
hugovk 60c3b671bf Be a good citizen https://snarky.ca/how-to-use-your-project-travis-to-help-test-python-itself/ 2017-10-19 23:58:20 +03:00
Hugo 5ae5f995cd Merge pull request #226 from pylast/more-tests
Add more tests and remove more dead Last.fm stuff
2017-10-19 23:53:10 +03:00
hugovk 8f3628a638 More tests 2017-10-19 23:37:10 +03:00
hugovk 85bf9f9928 Remove dead Last.fm track ban 2017-10-19 23:36:00 +03:00
hugovk f9a8bf3dae More tests 2017-10-19 00:55:20 +03:00
hugovk ebc5b29f5c Remove dead or broken Last.fm user functions 2017-10-19 00:46:23 +03:00
hugovk 5fd9e4c8c5 Remove dead or broken Last.fm user functions 2017-10-19 00:33:02 +03:00
hugovk 2ef88dfd4c Remove dead or broken Last.fm user functions 2017-10-19 00:22:06 +03:00
hugovk f2b699a11f More tests 2017-10-18 23:48:02 +03:00
hugovk f70254b947 Remove dead Last.fm tag search 2017-10-18 22:31:15 +03:00
hugovk 58b93c847e WIP: More tests 2017-10-18 08:57:39 +03:00
Hugo 641615f44f Merge pull request #225 from pylast/refactor-tests
Refactor tests
2017-10-18 00:12:46 +03:00
hugovk 5ba51c1e2c Finish refactor 2017-10-17 23:18:35 +03:00
hugovk 2edfc108d1 Fix test import 2017-10-17 23:14:00 +03:00
hugovk 2aa4dbdf88 Split Last.fm/Libre.fm tests 2017-10-17 21:57:17 +03:00
hugovk 31aeb6e69a Continue tests refactor 2017-10-17 21:45:20 +03:00
Hugo eac93bb84e Merge pull request #224 from pylast/rm-broken-lastfm-api-stuff
Remove dead Last.fm API stuff
2017-10-01 14:25:58 +03:00
Hugo 25f419204a Start tests refactor 2017-09-26 18:01:17 +03:00
Hugo a8522fded3 Bring back test_caching on another method 2017-09-26 11:04:02 +03:00
hugovk 0eac6e9ae2 Remove redundant functions 2017-09-26 09:14:24 +03:00
hugovk ec68660014 Remove dead Last.fm chart test 2017-09-26 09:08:40 +03:00
hugovk 27ba0dd6b3 Remove dead Last.fm metro methods 2017-09-26 08:58:37 +03:00
hugovk 230439f52f Remove dead Last.fm event/venu methods 2017-09-26 08:38:32 +03:00
Hugo 61216f73c0 Remove dead Last.fm artist/user methods 2017-09-25 18:36:02 +03:00
Hugo 454c519fd9 Remove dead Last.fm group methods 2017-09-25 18:29:12 +03:00
Hugo 6d738d3f43 Remove dead Last.fm artist/shout methods 2017-09-25 18:24:13 +03:00
Hugo 56e193d149 Remove dead Last.fm playlist methods 2017-09-25 18:11:48 +03:00
Hugo f419c39ef0 Remove dead Last.fm library methods 2017-09-25 18:03:00 +03:00
Hugo 3e097b98fc Remove @handle_lastfm_exceptions: dead Last.fm things will be removed 2017-09-25 17:49:12 +03:00
Hugo c303fd0139 Remove dead Last.fm attributes: releasedate in album.getInfo 2017-09-25 17:37:24 +03:00
Hugo 0d4f674ac7 Remove dead Last.fm methods: get_top_fans 2017-09-25 17:28:02 +03:00
Hugo d6281e3829 Merge pull request #217 from pylast/fix-collect-nodes
Fix: _collect_nodes() break if there are no child nodes
2017-09-25 14:54:09 +03:00
Hugo e19061a437 Re-enable test for re-added Last.fm API 2017-09-25 14:29:44 +03:00
Hugo 20d46a43b3 Merge pull request #223 from pylast/rm-redundant-code
Remove redundant code
2017-09-25 14:15:45 +03:00
Hugo fe6673ba29 Add more tests 2017-09-25 13:14:17 +03:00
Hugo 8c6c6aaab8 Remove broken and untested extract_items 2017-09-25 13:02:12 +03:00
Hugo 3be6a0504e Remove ununsed function _pad_list 2017-09-25 12:24:44 +03:00
Hugo fb1263a8dd Remove unused attributes 2017-09-25 10:49:52 +03:00
Hugo ebd0bb90b4 Remove unfinished function, it's out of scope of pylast 2017-09-25 10:43:01 +03:00
Hugo 5967590feb Remove parameter made redundant by removal of deprecated Scrobbler class 2017-09-25 10:40:04 +03:00
Hugo 60a892cc03 Merge pull request #222 from pylast/rm-redundant
Remove code rendered redundant after removing deprecated code
2017-09-19 18:18:47 +03:00
Hugo 13e965d3fd Remove code rendered redundant after removing deprecated code 2017-09-19 16:47:10 +03:00
Mice Pápai a565b7f159 Fix: _collect_nodes() break if there are no child nodes 2017-09-18 18:06:08 +03:00
Hugo b8306e0fd2 Merge pull request #207 from EdwardBetts/spelling
correct spelling mistake
2017-09-01 23:39:13 +03:00
Edward Betts b169556e4a correct spelling mistake 2017-09-01 20:41:59 +01:00
43 changed files with 5768 additions and 6659 deletions

7
.codecov.yml Normal file
View file

@ -0,0 +1,7 @@
# Documentation: https://docs.codecov.io/docs/codecov-yaml
codecov:
# Avoid "Missing base report"
# https://github.com/codecov/support/issues/363
# https://docs.codecov.io/v4.3.6/docs/comparing-commits
allow_coverage_offsets: true

View file

@ -11,7 +11,6 @@ charset = utf-8
[*.py]
indent_size = 4
indent_style = space
trim_trailing_whitespace = true
# Two-space indentation

1
.github/FUNDING.yml vendored Normal file
View file

@ -0,0 +1 @@
github: hugovk

22
.github/ISSUE_TEMPLATE.md vendored Normal file
View file

@ -0,0 +1,22 @@
### What did you do?
### What did you expect to happen?
### What actually happened?
### What versions are you using?
* OS:
* Python:
* pylast:
Please include **code** that reproduces the issue.
The [best reproductions](https://stackoverflow.com/help/minimal-reproducible-example)
are
[self-contained scripts](https://ericlippert.com/2014/03/05/how-to-debug-small-programs/)
with minimal dependencies.
```python
# code goes here
```

7
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View file

@ -0,0 +1,7 @@
Fixes #
Changes proposed in this pull request:
*
*
*

111
.github/labels.yml vendored Normal file
View file

@ -0,0 +1,111 @@
# Default GitHub labels
- color: d73a4a
description: "Something isn't working"
name: bug
- color: cfd3d7
description: "This issue or pull request already exists"
name: duplicate
- color: a2eeef
description: "New feature or request"
name: enhancement
- color: 7057ff
description: "Good for newcomers"
name: good first issue
- color: 008672
description: "Extra attention is needed"
name: help wanted
- color: e4e669
description: "This doesn't seem right"
name: invalid
- color: d876e3
description: "Further information is requested"
name: question
- color: ffffff
description: "This will not be worked on"
name: wontfix
# Keep a Changelog labels
# https://keepachangelog.com/en/1.0.0/
- color: 0e8a16
description: "For new features"
name: "changelog: Added"
- color: af99e5
description: "For changes in existing functionality"
name: "changelog: Changed"
- color: FFA500
description: "For soon-to-be removed features"
name: "changelog: Deprecated"
- color: 00A800
description: "For any bug fixes"
name: "changelog: Fixed"
- color: ff0000
description: "For now removed features"
name: "changelog: Removed"
- color: 045aa0
description: "In case of vulnerabilities"
name: "changelog: Security"
- color: fbca04
description: "Exclude PR from release draft"
name: "changelog: skip"
# Other labels
- color: e11d21
description: ""
name: Last.fm bug
- color: FFFFFF
description: ""
name: Milestone-0.3
- color: FFFFFF
description: ""
name: Performance
- color: FFFFFF
description: ""
name: Priority-High
- color: FFFFFF
description: ""
name: Priority-Low
- color: FFFFFF
description: ""
name: Priority-Medium
- color: FFFFFF
description: ""
name: Type-Other
- color: FFFFFF
description: ""
name: Type-Patch
- color: FFFFFF
description: ""
name: Usability
- color: 64c1c0
description: ""
name: backwards incompatible
- color: fef2c0
description: ""
name: build
- color: e99695
description: Feature that will be removed in the future
name: deprecation
- color: FFFFFF
description: ""
name: imported
- color: b60205
description: Removal of a feature, usually done in major releases
name: removal
- color: 0366d6
description: "For dependencies"
name: dependencies
- color: 0052cc
description: "Documentation"
name: docs
- color: f4660e
description: ""
name: Hacktoberfest
- color: f4660e
description: "To credit accepted Hacktoberfest PRs"
name: hacktoberfest-accepted
- color: d65e88
description: "Deploy and release"
name: release
- color: fef2c0
description: "Unit tests, linting, CI, etc."
name: test

48
.github/release-drafter.yml vendored Normal file
View file

@ -0,0 +1,48 @@
name-template: "$RESOLVED_VERSION"
tag-template: "$RESOLVED_VERSION"
categories:
- title: "Added"
labels:
- "changelog: Added"
- "enhancement"
- title: "Changed"
label: "changelog: Changed"
- title: "Deprecated"
label: "changelog: Deprecated"
- title: "Removed"
label: "changelog: Removed"
- title: "Fixed"
labels:
- "changelog: Fixed"
- "bug"
- title: "Security"
label: "changelog: Security"
exclude-labels:
- "changelog: skip"
autolabeler:
- label: "changelog: skip"
branch:
- "/pre-commit-ci-update-config/"
template: |
$CHANGES
version-resolver:
major:
labels:
- "changelog: Removed"
minor:
labels:
- "changelog: Added"
- "changelog: Changed"
- "changelog: Deprecated"
- "enhancement"
patch:
labels:
- "changelog: Fixed"
- "bug"
default: minor

13
.github/renovate.json vendored Normal file
View file

@ -0,0 +1,13 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:base"],
"labels": ["changelog: skip", "dependencies"],
"packageRules": [
{
"groupName": "github-actions",
"matchManagers": ["github-actions"],
"separateMajorMinor": "false"
}
],
"schedule": ["on the first day of the month"]
}

75
.github/workflows/deploy.yml vendored Normal file
View file

@ -0,0 +1,75 @@
name: Deploy
on:
push:
branches: [main]
tags: ["*"]
pull_request:
branches: [main]
release:
types:
- published
workflow_dispatch:
permissions:
contents: read
jobs:
# Always build & lint package.
build-package:
name: Build & verify package
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: hynek/build-and-inspect-python-package@v2
# Upload to Test PyPI on every commit on main.
release-test-pypi:
name: Publish in-dev package to test.pypi.org
if: |
github.repository_owner == 'pylast'
&& github.event_name == 'push'
&& github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
needs: build-package
permissions:
id-token: write
steps:
- name: Download packages built by build-and-inspect-python-package
uses: actions/download-artifact@v4
with:
name: Packages
path: dist
- name: Upload package to Test PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
# Upload to real PyPI on GitHub Releases.
release-pypi:
name: Publish released package to pypi.org
if: |
github.repository_owner == 'pylast'
&& github.event.action == 'published'
runs-on: ubuntu-latest
needs: build-package
permissions:
id-token: write
steps:
- name: Download packages built by build-and-inspect-python-package
uses: actions/download-artifact@v4
with:
name: Packages
path: dist
- name: Upload package to PyPI
uses: pypa/gh-action-pypi-publish@release/v1

23
.github/workflows/labels.yml vendored Normal file
View file

@ -0,0 +1,23 @@
name: Sync labels
permissions:
pull-requests: write
on:
push:
branches:
- main
paths:
- .github/labels.yml
workflow_dispatch:
jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: micnncim/action-label-syncer@v1
with:
prune: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

22
.github/workflows/lint.yml vendored Normal file
View file

@ -0,0 +1,22 @@
name: Lint
on: [push, pull_request, workflow_dispatch]
env:
FORCE_COLOR: 1
PIP_DISABLE_PIP_VERSION_CHECK: 1
permissions:
contents: read
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.x"
cache: pip
- uses: pre-commit/action@v3.0.1

34
.github/workflows/release-drafter.yml vendored Normal file
View file

@ -0,0 +1,34 @@
name: Release drafter
on:
push:
# branches to consider in the event; optional, defaults to all
branches:
- main
# pull_request event is required only for autolabeler
pull_request:
# Only following types are handled by the action, but one can default to all as well
types: [opened, reopened, synchronize]
# pull_request_target event is required for autolabeler to support PRs from forks
# pull_request_target:
# types: [opened, reopened, synchronize]
workflow_dispatch:
permissions:
contents: read
jobs:
update_release_draft:
if: github.repository_owner == 'pylast'
permissions:
# write permission is required to create a GitHub Release
contents: write
# write permission is required for autolabeler
# otherwise, read permission is required at least
pull-requests: write
runs-on: ubuntu-latest
steps:
# Drafts your next release notes as pull requests are merged into "main"
- uses: release-drafter/release-drafter@v6
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

22
.github/workflows/require-pr-label.yml vendored Normal file
View file

@ -0,0 +1,22 @@
name: Require PR label
on:
pull_request:
types: [opened, reopened, labeled, unlabeled, synchronize]
jobs:
label:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: mheap/github-action-required-labels@v5
with:
mode: minimum
count: 1
labels:
"changelog: Added, changelog: Changed, changelog: Deprecated, changelog:
Fixed, changelog: Removed, changelog: Security, changelog: skip"

54
.github/workflows/test.yml vendored Normal file
View file

@ -0,0 +1,54 @@
name: Test
on: [push, pull_request, workflow_dispatch]
env:
FORCE_COLOR: 1
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
python-version: ["pypy3.10", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
os: [ubuntu-latest]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
cache: pip
- name: Install dependencies
run: |
python -m pip install -U pip
python -m pip install -U wheel
python -m pip install -U tox
- name: Tox tests
run: |
tox -e py
env:
PYLAST_API_KEY: ${{ secrets.PYLAST_API_KEY }}
PYLAST_API_SECRET: ${{ secrets.PYLAST_API_SECRET }}
PYLAST_PASSWORD_HASH: ${{ secrets.PYLAST_PASSWORD_HASH }}
PYLAST_USERNAME: ${{ secrets.PYLAST_USERNAME }}
- name: Upload coverage
uses: codecov/codecov-action@v3.1.5
with:
flags: ${{ matrix.os }}
name: ${{ matrix.os }} Python ${{ matrix.python-version }}
success:
needs: test
runs-on: ubuntu-latest
name: Test successful
steps:
- name: Success
run: echo Test successful

6
.gitignore vendored
View file

@ -47,8 +47,9 @@ htmlcov/
.cache
nosetests.xml
coverage.xml
*,cover
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
@ -65,3 +66,6 @@ target/
# JetBrains
.idea/
# Clone Digger
output.html

74
.pre-commit-config.yaml Normal file
View file

@ -0,0 +1,74 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.5.0
hooks:
- id: ruff
args: [--exit-non-zero-on-fix]
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 24.4.2
hooks:
- id: black
- repo: https://github.com/asottile/blacken-docs
rev: 1.18.0
hooks:
- id: blacken-docs
args: [--target-version=py38]
additional_dependencies: [black]
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: check-added-large-files
- id: check-case-conflict
- id: check-merge-conflict
- id: check-json
- id: check-toml
- id: check-yaml
- id: debug-statements
- id: end-of-file-fixer
- id: forbid-submodules
- id: trailing-whitespace
exclude: .github/(ISSUE_TEMPLATE|PULL_REQUEST_TEMPLATE).md
- repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.28.6
hooks:
- id: check-github-workflows
- id: check-renovate
- repo: https://github.com/rhysd/actionlint
rev: v1.7.1
hooks:
- id: actionlint
- repo: https://github.com/tox-dev/pyproject-fmt
rev: 2.1.3
hooks:
- id: pyproject-fmt
- repo: https://github.com/abravalheri/validate-pyproject
rev: v0.18
hooks:
- id: validate-pyproject
- repo: https://github.com/tox-dev/tox-ini-fmt
rev: 1.3.1
hooks:
- id: tox-ini-fmt
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v4.0.0-alpha.8
hooks:
- id: prettier
args: [--prose-wrap=always, --print-width=88]
exclude: .github/(ISSUE_TEMPLATE|PULL_REQUEST_TEMPLATE).md
- repo: meta
hooks:
- id: check-hooks-apply
- id: check-useless-excludes
ci:
autoupdate_schedule: quarterly

View file

@ -1,9 +0,0 @@
checks:
python:
code_rating: true
duplicate_code: true
filter:
excluded_paths:
- '*/test/*'
tools:
external_code_coverage: true

View file

@ -1,51 +0,0 @@
language: python
cache: pip
env:
global:
- secure: ivg6II471E9HV8xyqnawLIuP/sZ0J63Y+BC0BQcRVKtLn/K3zmD1ozM3TFL9S549Nxd0FqDKHXJvXsgaTGIDpK8sxE2AMKV5IojyM0iAVuN7YjPK9vwSlRw1u0EysPMFqxOZVQnoDyHrSGIUrP/VMdnhBu6dbUX0FyEkvZshXhY=
- secure: gDWNEYA1EUv4G230/KzcTgcmEST0nf2FeW/z/prsoQBu+TWw1rKKSJAJeMLvuI1z4aYqqNYdmqjWyNhhVK3p5wmFP2lxbhaBT1jDsxxFpePc0nUkdAQOOD0yBpbBGkqkjjxU34HjTX2NFNEbcM3izVVE9oQmS5r4oFFNJgdL91c=
- secure: RpsZblHFU7a5dnkO/JUgi70RkNJwoUh3jJqVo1oOLjL+lvuAmPXhI8MDk2diUk43X+XCBFBEnm7UCGnjUF+hDnobO4T+VrIFuVJWg3C7iKIT+YWvgG6A+CSeo/P0I0dAeUscTr5z4ylOq3EDx4MFSa8DmoWMmjKTAG1GAeTlY2k=
- secure: T5OKyd5Bs0nZbUr+YICbThC5GrFq/kUjX8FokzCv7NWsYaUWIwEmMXXzoYALoB3A+rAglOx6GABaupoNKKg3tFQyxXphuMKpZ8MasMAMFjFW0d7wsgGy0ylhVwrgoKzDbCQ5FKbohC+9ltLs+kKMCQ0L+MI70a/zTfF4/dVWO/o=
- secure: DxBvGGoIgbAeuuU3A6+J1HBbmUAEvqdmK73etw+yNKDLGvvukgTL33dNCr8CZXLKRRvfhrjU7Q01GUpOTxrVQ9nJgsD55kwx0wPtuBWIF80M2m4SPsiVLlwP/LFYD5JMDTDWjFTlVahma8P7qoLjCc7b/RgigWLidH19snQmjdY=
- secure: VPARlWNg/0Nit7a924vJlDfv7yiuTDtrcGZNFrZ6yN3dl8ZjVPizQXQNKA3yq0y2jW25nwjRwZYj3eY5MdM9F7Sw51d+/8AjFtdCuRgDvwlQFR/pCoyzqgJATkXKo7mlejvnA+5EKUzAmu3drIbboFgbLgRTMrG7b/ot9tazTHs=
- secure: CQYL7MH6tSVrCcluIfWfDSTo4E/p+9pF0eI7Vtf0oaZBzyulODHK8h/mzJp4HwezyfOu0RCedq6sloGQr1/29CvWWESaYyoGoGz9Mz2ZS+MpIcjGISfZa+x4vSp6QPFvd4i/1Z/1j2gJVVyswkrIVUwZIDJtfAKzZI5iHx2gH8Y=
- secure: SsKJoJwtDVWrL5xxl9C/gTRy6FhfRQQNNAFOogl9mTs/WeI2t9QTYoKsxLPXOdoRdu4MvT3h/B2sjwggt7zP81fBVxQRTkg4nq0zSHlj0NqclbFa6I5lUYdGwH9gPk/HWJJwXhKRDsqn/iRw2v+qBDs/j3kIgPQ0yjM58LEPXic=
matrix:
include:
- python: 2.7
env: TOXENV=py2lint
- python: 2.7
env: TOXENV=py27
- python: 3.6
env: TOXENV=py3lint
- python: 3.6
env: TOXENV=py36
- python: 3.5
env: TOXENV=py35
- python: 3.4
env: TOXENV=py34
- python: pypy3
env: TOXENV=pypy3
- python: pypy
env: TOXENV=pypy
allow_failures:
- env: TOXENV=pypy
- env: TOXENV=pypy3
fast_finish: true
sudo: false
install:
- travis_retry pip install tox
- travis_retry pip install coverage
script: tox
after_success:
- travis_retry pip install coveralls && coveralls
- travis_retry pip install codecov && codecov
- travis_retry pip install scrutinizer-ocular && ocular

159
CHANGELOG.md Normal file
View file

@ -0,0 +1,159 @@
# Changelog
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## 4.2.1 and newer
See GitHub Releases:
- https://github.com/pylast/pylast/releases
## [4.2.0] - 2021-03-14
## Changed
- Fix unsafe creation of temp file for caching, and improve exception raising (#356)
@kvanzuijlen
- [pre-commit.ci] pre-commit autoupdate (#362) @pre-commit-ci
## [4.1.0] - 2021-01-04
## Added
- Add support for streaming (#336) @kvanzuijlen
- Add Python 3.9 final to Travis CI (#350) @sheetalsingala
## Changed
- Update copyright year (#360) @hugovk
- Replace Travis CI with GitHub Actions (#352) @hugovk
- [pre-commit.ci] pre-commit autoupdate (#359) @pre-commit-ci
## Fixed
- Set limit to 50 by default, not 1 (#355) @hugovk
## [4.0.0] - 2020-10-07
## Added
- Add support for Python 3.9 (#347) @hugovk
## Removed
- Remove deprecated `Artist.get_cover_image`, `User.get_artist_tracks` and
`STATUS_TOKEN_ERROR` (#348) @hugovk
- Drop support for EOL Python 3.5 (#346) @hugovk
## [3.3.0] - 2020-06-25
### Added
- `User.get_now_playing`: Add album and cover image to info (#330) @hugovk
### Changed
- Improve handling of error responses from the API (#327) @spiritualized
### Deprecated
- Deprecate `Artist.get_cover_image`, they're no longer available from Last.fm (#332)
@hugovk
### Fixed
- Fix `artist.get_bio_content()` to return `None` if bio is empty (#326) @hugovk
## [3.2.1] - 2020-03-05
### Fixed
- Only Python 3 is supported: don't create universal wheel (#318) @hugovk
- Fix regression calling `get_recent_tracks` with `limit=None` (#320) @hugovk
- Fix `DeprecationWarning`: Please use `assertRegex` instead (#323) @hugovk
## [3.2.0] - 2020-01-03
### Added
- Support for Python 3.8
- Store album art URLs when you call `GetTopAlbums` ([#307])
- Retry paging through results on exception ([#297])
- More error status codes from https://last.fm/api/errorcodes ([#297])
### Changed
- Respect `get_recent_tracks`' limit when there's a now playing track ([#310])
- Move installable code to `src/` ([#301])
- Update `get_weekly_artist_charts` docstring: only for `User` ([#311])
- Remove Python 2 warnings, `python_requires` should be enough ([#312])
- Use setuptools_scm to simplify versioning during release ([#316])
- Various lint and test updates
### Deprecated
- Last.fm's `user.getArtistTracks` has now been deprecated by Last.fm and is no longer
available. Last.fm returns a "Deprecated - This type of request is no longer
supported" error when calling it. A future version of pylast will remove its
`User.get_artist_tracks` altogether. ([#305])
- `STATUS_TOKEN_ERROR` is deprecated and will be removed in a future version. Use
`STATUS_OPERATION_FAILED` instead.
## [3.1.0] - 2019-03-07
### Added
- Extract username from session via new
`SessionKeyGenerator.get_web_auth_session_key_username` ([#290])
- `User.get_track_scrobbles` ([#298])
### Deprecated
- `User.get_artist_tracks`. Use `User.get_track_scrobbles` as a partial replacement.
([#298])
## [3.0.0] - 2019-01-01
### Added
- This changelog file ([#273])
### Removed
- Support for Python 2.7 ([#265])
- Constants `COVER_SMALL`, `COVER_MEDIUM`, `COVER_LARGE`, `COVER_EXTRA_LARGE` and
`COVER_MEGA`. Use `SIZE_SMALL` etc. instead. ([#282])
## [2.4.0] - 2018-08-08
### Deprecated
- Support for Python 2.7 ([#265])
[4.2.0]: https://github.com/pylast/pylast/compare/4.1.0...4.2.0
[4.1.0]: https://github.com/pylast/pylast/compare/4.0.0...4.1.0
[4.0.0]: https://github.com/pylast/pylast/compare/3.3.0...4.0.0
[3.3.0]: https://github.com/pylast/pylast/compare/3.2.1...3.3.0
[3.2.1]: https://github.com/pylast/pylast/compare/3.2.0...3.2.1
[3.2.0]: https://github.com/pylast/pylast/compare/3.1.0...3.2.0
[3.1.0]: https://github.com/pylast/pylast/compare/3.0.0...3.1.0
[3.0.0]: https://github.com/pylast/pylast/compare/2.4.0...3.0.0
[2.4.0]: https://github.com/pylast/pylast/compare/2.3.0...2.4.0
[#265]: https://github.com/pylast/pylast/issues/265
[#273]: https://github.com/pylast/pylast/issues/273
[#282]: https://github.com/pylast/pylast/pull/282
[#290]: https://github.com/pylast/pylast/pull/290
[#297]: https://github.com/pylast/pylast/issues/297
[#298]: https://github.com/pylast/pylast/issues/298
[#301]: https://github.com/pylast/pylast/issues/301
[#305]: https://github.com/pylast/pylast/issues/305
[#307]: https://github.com/pylast/pylast/issues/307
[#310]: https://github.com/pylast/pylast/issues/310
[#311]: https://github.com/pylast/pylast/issues/311
[#312]: https://github.com/pylast/pylast/issues/312
[#316]: https://github.com/pylast/pylast/issues/316
[#346]: https://github.com/pylast/pylast/issues/346
[#347]: https://github.com/pylast/pylast/issues/347
[#348]: https://github.com/pylast/pylast/issues/348

View file

@ -1,6 +1,6 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
https://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
@ -32,11 +32,11 @@ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
You must give any other recipients of the Work or Derivative Works a copy of this License; and
You must give any other recipients of the Work or Derivative Works a copy of this License; and
You must cause any modified files to carry prominent notices stating that You changed the files; and
You must cause any modified files to carry prominent notices stating that You changed the files; and
You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.

View file

@ -1,4 +0,0 @@
Installation Instructions
=========================
Execute "python setup.py install" as a super user.

View file

@ -1,6 +0,0 @@
include pylast/__init__.py
include setup.py
include README.md
include COPYING
include INSTALL
recursive-include tests *.py

179
README.md
View file

@ -1,50 +1,65 @@
pyLast
======
# pyLast
[![Build status](https://travis-ci.org/pylast/pylast.svg?branch=develop)](https://travis-ci.org/pylast/pylast)
[![PyPI version](https://img.shields.io/pypi/v/pylast.svg)](https://pypi.python.org/pypi/pylast/)
<!--[![PyPI downloads](https://img.shields.io/pypi/dm/pylast.svg)](https://pypi.python.org/pypi/pylast/)-->
[![Coverage (Codecov)](https://codecov.io/gh/pylast/pylast/branch/develop/graph/badge.svg)](https://codecov.io/gh/pylast/pylast)
[![Coverage (Coveralls)](https://coveralls.io/repos/github/pylast/pylast/badge.svg?branch=develop)](https://coveralls.io/github/pylast/pylast?branch=develop)
[![Code health](https://landscape.io/github/pylast/pylast/develop/landscape.svg)](https://landscape.io/github/hugovk/pylast/develop)
[![PyPI version](https://img.shields.io/pypi/v/pylast.svg)](https://pypi.org/project/pylast/)
[![Supported Python versions](https://img.shields.io/pypi/pyversions/pylast.svg)](https://pypi.org/project/pylast/)
[![PyPI downloads](https://img.shields.io/pypi/dm/pylast.svg)](https://pypistats.org/packages/pylast)
[![Test](https://github.com/pylast/pylast/workflows/Test/badge.svg)](https://github.com/pylast/pylast/actions)
[![Coverage (Codecov)](https://codecov.io/gh/pylast/pylast/branch/main/graph/badge.svg)](https://codecov.io/gh/pylast/pylast)
[![Code style: Black](https://img.shields.io/badge/code%20style-Black-000000.svg)](https://github.com/psf/black)
[![DOI](https://zenodo.org/badge/7803088.svg)](https://zenodo.org/badge/latestdoi/7803088)
A Python interface to [Last.fm](https://www.last.fm/) and other API-compatible websites
such as [Libre.fm](https://libre.fm/).
A Python interface to [Last.fm](https://www.last.fm/) and other API-compatible websites such as [Libre.fm](https://libre.fm/).
Use the pydoc utility for help on usage or see [tests/](tests/) for examples.
Try using the pydoc utility for help on usage or see [test_pylast.py](tests/test_pylast.py) for examples.
## Installation
Installation
------------
Install latest development version:
Install via pip:
```sh
python3 -m pip install -U git+https://git.hirad.it/Hirad/pylast
```
pip install pylast
Or from requirements.txt:
```txt
-e https://git.hirad.it/Hirad/pylast#egg=pylast
```
Note:
* pyLast >= 2.0.0 supports Python 2.7.10+ and 3.4, 3.5, 3.6.
* pyLast >= 1.7.0 < 2.0.0 supports Python 2.7, 3.3, 3.4, 3.5, 3.6.
* pyLast >= 1.0.0 < 1.7.0 supports Python 2.7, 3.3, 3.4.
* pyLast >= 0.5 < 1.0.0 supports Python 2, 3.
* pyLast < 0.5 supports Python 2.
- pyLast 5.3+ supports Python 3.8-3.13.
- pyLast 5.2+ supports Python 3.8-3.12.
- pyLast 5.1 supports Python 3.7-3.11.
- pyLast 5.0 supports Python 3.7-3.10.
- pyLast 4.3 - 4.5 supports Python 3.6-3.10.
- pyLast 4.0 - 4.2 supports Python 3.6-3.9.
- pyLast 3.2 - 3.3 supports Python 3.5-3.8.
- pyLast 3.0 - 3.1 supports Python 3.5-3.7.
- pyLast 2.2 - 2.4 supports Python 2.7.10+, 3.4-3.7.
- pyLast 2.0 - 2.1 supports Python 2.7.10+, 3.4-3.6.
- pyLast 1.7 - 1.9 supports Python 2.7, 3.3-3.6.
- pyLast 1.0 - 1.6 supports Python 2.7, 3.3-3.4.
- pyLast 0.5 supports Python 2, 3.
- pyLast < 0.5 supports Python 2.
Features
--------
## Features
* Simple public interface.
* Access to all the data exposed by the Last.fm web services.
* Scrobbling support.
* Full object-oriented design.
* Proxy support.
* Internal caching support for some web services calls (disabled by default).
* Support for other API-compatible networks like Libre.fm.
* Python 3-friendly (Starting from 0.5).
- Simple public interface.
- Access to all the data exposed by the Last.fm web services.
- Scrobbling support.
- Full object-oriented design.
- Proxy support.
- Internal caching support for some web services calls (disabled by default).
- Support for other API-compatible networks like Libre.fm.
## Getting started
Getting Started
---------------
Here's some simple code example to get you started. In order to create any object from pyLast, you need a `Network` object which represents a social music network that is Last.fm or any other API-compatible one. You can obtain a pre-configured one for Last.fm and use it as follows:
Here's some simple code example to get you started. In order to create any object from
pyLast, you need a `Network` object which represents a social music network that is
Last.fm or any other API-compatible one. You can obtain a pre-configured one for Last.fm
and use it as follows:
```python
import pylast
@ -58,14 +73,50 @@ API_SECRET = "425b55975eed76058ac220b7b4e8a054"
username = "your_user_name"
password_hash = pylast.md5("your_password")
network = pylast.LastFMNetwork(api_key=API_KEY, api_secret=API_SECRET,
username=username, password_hash=password_hash)
network = pylast.LastFMNetwork(
api_key=API_KEY,
api_secret=API_SECRET,
username=username,
password_hash=password_hash,
)
```
Alternatively, instead of creating `network` with a username and password, you can
authenticate with a session key:
```python
import pylast
SESSION_KEY_FILE = os.path.join(os.path.expanduser("~"), ".session_key")
network = pylast.LastFMNetwork(API_KEY, API_SECRET)
if not os.path.exists(SESSION_KEY_FILE):
skg = pylast.SessionKeyGenerator(network)
url = skg.get_web_auth_url()
print(f"Please authorize this script to access your account: {url}\n")
import time
import webbrowser
webbrowser.open(url)
while True:
try:
session_key = skg.get_web_auth_session_key(url)
with open(SESSION_KEY_FILE, "w") as f:
f.write(session_key)
break
except pylast.WSError:
time.sleep(1)
else:
session_key = open(SESSION_KEY_FILE).read()
network.session_key = session_key
```
And away we go:
```python
# Now you can use that object everywhere
artist = network.get_artist("System of a Down")
artist.shout("<3")
track = network.get_track("Iron Maiden", "The Nomad")
track.love()
track.add_tags(("awesome", "favorite"))
@ -74,14 +125,20 @@ track.add_tags(("awesome", "favorite"))
# to get more help about anything and see examples of how it works
```
More examples in <a href="https://github.com/hugovk/lastfm-tools">hugovk/lastfm-tools</a> and [test_pylast.py](test_pylast.py).
More examples in
<a href="https://github.com/hugovk/lastfm-tools">hugovk/lastfm-tools</a> and
[tests/](https://github.com/pylast/pylast/tree/main/tests).
Testing
-------
## Testing
[tests/test_pylast.py](tests/test_pylast.py) contains integration tests with Last.fm, and plenty of code examples. Unit tests are also in the [tests/](tests/) directory.
The [tests/](https://github.com/pylast/pylast/tree/main/tests) directory contains
integration and unit tests with Last.fm, and plenty of code examples.
For integration tests you need a test account at Last.fm that will become cluttered with test data, and an API key and secret. Either copy [example_test_pylast.yaml](example_test_pylast.yaml) to test_pylast.yaml and fill out the credentials, or set them as environment variables like:
For integration tests you need a test account at Last.fm that will become cluttered with
test data, and an API key and secret. Either copy
[example_test_pylast.yaml](https://github.com/pylast/pylast/blob/main/example_test_pylast.yaml)
to test_pylast.yaml and fill out the credentials, or set them as environment variables
like:
```sh
export PYLAST_USERNAME=TODO_ENTER_YOURS_HERE
@ -91,20 +148,46 @@ export PYLAST_API_SECRET=TODO_ENTER_YOURS_HERE
```
To run all unit and integration tests:
```sh
pip install pytest flaky mock
py.test
python3 -m pip install -e ".[tests]"
pytest
```
Or run just one test case:
```sh
py.test -k test_scrobble
pytest -k test_scrobble
```
To run with coverage:
```sh
py.test -v --cov pylast --cov-report term-missing
pytest -v --cov pylast --cov-report term-missing
coverage report # for command-line report
coverage html # for HTML report
open htmlcov/index.html
```
## Logging
To enable from your own code:
```python
import logging
import pylast
logging.basicConfig(level=logging.INFO)
network = pylast.LastFMNetwork(...)
```
To enable from pytest:
```sh
pytest --log-cli-level info -k test_album_search_images
```
To also see data returned from the API, use `level=logging.DEBUG` or
`--log-cli-level debug` instead.

View file

@ -1,31 +1,23 @@
# Release Checklist
* [ ] Get [master to the appropriate code release state](https://github.com/pylast/pylast/compare/master...develop?expand=1). [Travis CI](https://travis-ci.org/pylast/pylast) should be running cleanly for all merges to master.
* [ ] Update version in `pylast/__init__.py` and `setup.py` and commit:
- [ ] Get `main` to the appropriate code release state.
[GitHub Actions](https://github.com/pylast/pylast/actions) should be running
cleanly for all merges to `main`.
[![Test](https://github.com/pylast/pylast/workflows/Test/badge.svg)](https://github.com/pylast/pylast/actions)
- [ ] Edit release draft, adjust text if needed:
https://github.com/pylast/pylast/releases
- [ ] Check next tag is correct, amend if needed
- [ ] Publish release
- [ ] Check the tagged
[GitHub Actions build](https://github.com/pylast/pylast/actions/workflows/deploy.yml)
has deployed to [PyPI](https://pypi.org/project/pylast/#history)
- [ ] Check installation:
```bash
git checkout master
edit pylast/__init__.py setup.py
git add pylast/__init__.py setup.py
git commit -m "Release 1.8.0"
```
* [ ] Tag the last commit with the version number:
```bash
git tag -a 1.8.0 -m "Release 1.8.0"
```
* [ ] Create a distribution and release on PyPI:
```bash
python setup.py sdist --format=gztar
twine upload dist/pylast-1.8.0.tar.gz
```
* [ ] Check installation: `pip install -U pylast`
* [ ] Push: `git push`
* [ ] Push tags: `git push --tags`
* [ ] Create new GitHub release: https://github.com/pylast/pylast/releases/new
* Tag: Pick existing tag "1.8.0"
* Title: "Release 1.8.0"
* [ ] Update develop branch from master:
```bash
git checkout develop
git merge master --ff-only
git push
pip3 uninstall -y pylast && pip3 install -U pylast && python3 -c "import pylast; print(pylast.__version__)"
```

View file

@ -1,4 +0,0 @@
#!/usr/bin/env bash
clonedigger pylast
grep -E "Clones detected|lines are duplicates" output.html
exit 0

View file

@ -1,4 +1,4 @@
username: TODO_ENTER_YOURS_HERE
password_hash: TODO_ENTER_YOURS_HERE
api_key: TODO_ENTER_YOURS_HERE
api_secret: TODO_ENTER_YOURS_HERE
username: TODO_ENTER_YOURS_HERE
password_hash: TODO_ENTER_YOURS_HERE
api_key: TODO_ENTER_YOURS_HERE
api_secret: TODO_ENTER_YOURS_HERE

File diff suppressed because it is too large Load diff

97
pyproject.toml Normal file
View file

@ -0,0 +1,97 @@
[build-system]
build-backend = "hatchling.build"
requires = [
"hatch-vcs",
"hatchling",
]
[project]
name = "pylast"
description = "A Python interface to Last.fm and Libre.fm"
readme = "README.md"
keywords = [
"Last.fm",
"music",
"scrobble",
"scrobbling",
]
license = { text = "Apache-2.0" }
maintainers = [
{ name = "Hugo van Kemenade" },
]
authors = [
{ name = "Amr Hassan <amr.hassan@gmail.com> and Contributors", email = "amr.hassan@gmail.com" },
]
requires-python = ">=3.8"
classifiers = [
"Development Status :: 5 - Production/Stable",
"License :: OSI Approved :: Apache Software License",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
"Topic :: Internet",
"Topic :: Multimedia :: Sound/Audio",
"Topic :: Software Development :: Libraries :: Python Modules",
]
dynamic = [
"version",
]
dependencies = [
"httpx",
]
optional-dependencies.tests = [
"flaky",
"pytest",
"pytest-cov",
"pytest-random-order",
"pyyaml",
]
urls.Changelog = "https://github.com/pylast/pylast/releases"
urls.Homepage = "https://github.com/pylast/pylast"
urls.Source = "https://github.com/pylast/pylast"
[tool.hatch]
version.source = "vcs"
[tool.hatch.version.raw-options]
local_scheme = "no-local-version"
[tool.ruff]
fix = true
lint.select = [
"C4", # flake8-comprehensions
"E", # pycodestyle errors
"EM", # flake8-errmsg
"F", # pyflakes errors
"I", # isort
"ISC", # flake8-implicit-str-concat
"LOG", # flake8-logging
"PGH", # pygrep-hooks
"RUF022", # unsorted-dunder-all
"RUF100", # unused noqa (yesqa)
"UP", # pyupgrade
"W", # pycodestyle warnings
"YTT", # flake8-2020
]
lint.extend-ignore = [
"E203", # Whitespace before ':'
"E221", # Multiple spaces before operator
"E226", # Missing whitespace around arithmetic operator
"E241", # Multiple spaces after ','
]
lint.isort.known-first-party = [
"pylast",
]
lint.isort.required-imports = [
"from __future__ import annotations",
]
[tool.pyproject-fmt]
max_supported_python = "3.13"

6
pytest.ini Normal file
View file

@ -0,0 +1,6 @@
[pytest]
filterwarnings =
once::DeprecationWarning
once::PendingDeprecationWarning
xfail_strict=true

View file

@ -1,34 +0,0 @@
#!/usr/bin/env python
from setuptools import setup, find_packages
setup(
name="pylast",
version="1.9.0",
author="Amr Hassan <amr.hassan@gmail.com>",
install_requires=['six'],
tests_require=['mock', 'pytest', 'coverage', 'pep8', 'pyyaml', 'pyflakes'],
description=("A Python interface to Last.fm and Libre.fm"),
author_email="amr.hassan@gmail.com",
url="https://github.com/pylast/pylast",
classifiers=[
"Development Status :: 5 - Production/Stable",
"License :: OSI Approved :: Apache Software License",
"Topic :: Internet",
"Topic :: Multimedia :: Sound/Audio",
"Topic :: Software Development :: Libraries :: Python Modules",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
],
keywords=["Last.fm", "music", "scrobble", "scrobbling"],
packages=find_packages(exclude=('tests*',)),
license="Apache2"
)
# End of file

2921
src/pylast/__init__.py Normal file

File diff suppressed because it is too large Load diff

120
tests/test_album.py Executable file
View file

@ -0,0 +1,120 @@
#!/usr/bin/env python
"""
Integration (not unit) tests for pylast.py
"""
from __future__ import annotations
import pylast
from .test_pylast import TestPyLastWithLastFm
class TestPyLastAlbum(TestPyLastWithLastFm):
def test_album_tags_are_topitems(self) -> None:
# Arrange
album = self.network.get_album("Test Artist", "Test Album")
# Act
tags = album.get_top_tags(limit=1)
# Assert
assert len(tags) > 0
assert isinstance(tags[0], pylast.TopItem)
def test_album_is_hashable(self) -> None:
# Arrange
album = self.network.get_album("Test Artist", "Test Album")
# Act/Assert
self.helper_is_thing_hashable(album)
def test_album_in_recent_tracks(self) -> None:
# Arrange
lastfm_user = self.network.get_user(self.username)
# Act
# limit=2 to ignore now-playing:
track = list(lastfm_user.get_recent_tracks(limit=2))[0]
# Assert
assert hasattr(track, "album")
def test_album_wiki_content(self) -> None:
# Arrange
album = pylast.Album("Test Artist", "Test Album", self.network)
# Act
wiki = album.get_wiki_content()
# Assert
assert wiki is not None
assert len(wiki) >= 1
def test_album_wiki_published_date(self) -> None:
# Arrange
album = pylast.Album("Test Artist", "Test Album", self.network)
# Act
wiki = album.get_wiki_published_date()
# Assert
assert wiki is not None
assert len(wiki) >= 1
def test_album_wiki_summary(self) -> None:
# Arrange
album = pylast.Album("Test Artist", "Test Album", self.network)
# Act
wiki = album.get_wiki_summary()
# Assert
assert wiki is not None
assert len(wiki) >= 1
def test_album_eq_none_is_false(self) -> None:
# Arrange
album1 = None
album2 = pylast.Album("Test Artist", "Test Album", self.network)
# Act / Assert
assert album1 != album2
def test_album_ne_none_is_true(self) -> None:
# Arrange
album1 = None
album2 = pylast.Album("Test Artist", "Test Album", self.network)
# Act / Assert
assert album1 != album2
def test_get_cover_image(self) -> None:
# Arrange
album = self.network.get_album("Test Artist", "Test Album")
# Act
image = album.get_cover_image()
# Assert
assert image.startswith("https://")
assert image.endswith(".gif") or image.endswith(".png")
def test_mbid(self) -> None:
# Arrange
album = self.network.get_album("Radiohead", "OK Computer")
# Act
mbid = album.get_mbid()
# Assert
assert mbid == "0b6b4ba0-d36f-47bd-b4ea-6a5b91842d29"
def test_no_mbid(self) -> None:
# Arrange
album = self.network.get_album("Test Artist", "Test Album")
# Act
mbid = album.get_mbid()
# Assert
assert mbid is None

278
tests/test_artist.py Executable file
View file

@ -0,0 +1,278 @@
#!/usr/bin/env python
"""
Integration (not unit) tests for pylast.py
"""
from __future__ import annotations
import pytest
import pylast
from .test_pylast import WRITE_TEST, TestPyLastWithLastFm
class TestPyLastArtist(TestPyLastWithLastFm):
def test_repr(self) -> None:
# Arrange
artist = pylast.Artist("Test Artist", self.network)
# Act
representation = repr(artist)
# Assert
assert representation.startswith("pylast.Artist('Test Artist',")
def test_artist_is_hashable(self) -> None:
# Arrange
test_artist = self.network.get_artist("Radiohead")
artist = test_artist.get_similar(limit=2)[0].item
assert isinstance(artist, pylast.Artist)
# Act/Assert
self.helper_is_thing_hashable(artist)
def test_bio_published_date(self) -> None:
# Arrange
artist = pylast.Artist("Test Artist", self.network)
# Act
bio = artist.get_bio_published_date()
# Assert
assert bio is not None
assert len(bio) >= 1
def test_bio_content(self) -> None:
# Arrange
artist = pylast.Artist("Test Artist", self.network)
# Act
bio = artist.get_bio_content(language="en")
# Assert
assert bio is not None
assert len(bio) >= 1
def test_bio_content_none(self) -> None:
# Arrange
# An artist with no biography, with "<content/>" in the API XML
artist = pylast.Artist("Mr Sizef + Unquote", self.network)
# Act
bio = artist.get_bio_content()
# Assert
assert bio is None
def test_bio_summary(self) -> None:
# Arrange
artist = pylast.Artist("Test Artist", self.network)
# Act
bio = artist.get_bio_summary(language="en")
# Assert
assert bio is not None
assert len(bio) >= 1
def test_artist_top_tracks(self) -> None:
# Arrange
# Pick an artist with plenty of plays
artist = self.network.get_top_artists(limit=1)[0].item
# Act
things = artist.get_top_tracks(limit=2)
# Assert
self.helper_two_different_things_in_top_list(things, pylast.Track)
def test_artist_top_albums(self) -> None:
# Arrange
# Pick an artist with plenty of plays
artist = self.network.get_top_artists(limit=1)[0].item
# Act
things = list(artist.get_top_albums(limit=2))
# Assert
self.helper_two_different_things_in_top_list(things, pylast.Album)
@pytest.mark.parametrize("test_limit", [1, 50, 100])
def test_artist_top_albums_limit(self, test_limit: int) -> None:
# Arrange
# Pick an artist with plenty of plays
artist = self.network.get_top_artists(limit=1)[0].item
# Act
things = artist.get_top_albums(limit=test_limit)
# Assert
assert len(things) == test_limit
def test_artist_top_albums_limit_default(self) -> None:
# Arrange
# Pick an artist with plenty of plays
artist = self.network.get_top_artists(limit=1)[0].item
# Act
things = artist.get_top_albums()
# Assert
assert len(things) == 50
def test_artist_listener_count(self) -> None:
# Arrange
artist = self.network.get_artist("Test Artist")
# Act
count = artist.get_listener_count()
# Assert
assert isinstance(count, int)
assert count > 0
@pytest.mark.skipif(not WRITE_TEST, reason="Only test once to avoid collisions")
def test_tag_artist(self) -> None:
# Arrange
artist = self.network.get_artist("Test Artist")
# artist.clear_tags()
# Act
artist.add_tag("testing")
# Assert
tags = artist.get_tags()
assert len(tags) > 0
found = any(tag.name == "testing" for tag in tags)
assert found
@pytest.mark.skipif(not WRITE_TEST, reason="Only test once to avoid collisions")
def test_remove_tag_of_type_text(self) -> None:
# Arrange
tag = "testing" # text
artist = self.network.get_artist("Test Artist")
artist.add_tag(tag)
# Act
artist.remove_tag(tag)
# Assert
tags = artist.get_tags()
found = any(tag.name == "testing" for tag in tags)
assert not found
@pytest.mark.skipif(not WRITE_TEST, reason="Only test once to avoid collisions")
def test_remove_tag_of_type_tag(self) -> None:
# Arrange
tag = pylast.Tag("testing", self.network) # Tag
artist = self.network.get_artist("Test Artist")
artist.add_tag(tag)
# Act
artist.remove_tag(tag)
# Assert
tags = artist.get_tags()
found = any(tag.name == "testing" for tag in tags)
assert not found
@pytest.mark.skipif(not WRITE_TEST, reason="Only test once to avoid collisions")
def test_remove_tags(self) -> None:
# Arrange
tags = ["removetag1", "removetag2"]
artist = self.network.get_artist("Test Artist")
artist.add_tags(tags)
artist.add_tags("1more")
tags_before = artist.get_tags()
# Act
artist.remove_tags(tags)
# Assert
tags_after = artist.get_tags()
assert len(tags_after) == len(tags_before) - 2
found1 = any(tag.name == "removetag1" for tag in tags_after)
found2 = any(tag.name == "removetag2" for tag in tags_after)
assert not found1
assert not found2
@pytest.mark.skipif(not WRITE_TEST, reason="Only test once to avoid collisions")
def test_set_tags(self) -> None:
# Arrange
tags = ["sometag1", "sometag2"]
artist = self.network.get_artist("Test Artist 2")
artist.add_tags(tags)
tags_before = artist.get_tags()
new_tags = ["settag1", "settag2"]
# Act
artist.set_tags(new_tags)
# Assert
tags_after = artist.get_tags()
assert tags_before != tags_after
assert len(tags_after) == 2
found1, found2 = False, False
for tag in tags_after:
if tag.name == "settag1":
found1 = True
elif tag.name == "settag2":
found2 = True
assert found1
assert found2
def test_artists(self) -> None:
# Arrange
artist1 = self.network.get_artist("Radiohead")
artist2 = self.network.get_artist("Portishead")
# Act
url = artist1.get_url()
mbid = artist1.get_mbid()
playcount = artist1.get_playcount()
name = artist1.get_name(properly_capitalized=False)
name_cap = artist1.get_name(properly_capitalized=True)
# Assert
assert playcount > 1
assert artist1 != artist2
assert name.lower() == name_cap.lower()
assert url == "https://www.last.fm/music/radiohead"
assert mbid == "a74b1b7f-71a5-4011-9441-d0b5e4122711"
def test_artist_eq_none_is_false(self) -> None:
# Arrange
artist1 = None
artist2 = pylast.Artist("Test Artist", self.network)
# Act / Assert
assert artist1 != artist2
def test_artist_ne_none_is_true(self) -> None:
# Arrange
artist1 = None
artist2 = pylast.Artist("Test Artist", self.network)
# Act / Assert
assert artist1 != artist2
def test_artist_get_correction(self) -> None:
# Arrange
artist = pylast.Artist("guns and roses", self.network)
# Act
corrected_artist_name = artist.get_correction()
# Assert
assert corrected_artist_name == "Guns N' Roses"
def test_get_userplaycount(self) -> None:
# Arrange
artist = pylast.Artist("John Lennon", self.network, username=self.username)
# Act
playcount = artist.get_userplaycount()
# Assert
assert playcount >= 0

36
tests/test_country.py Executable file
View file

@ -0,0 +1,36 @@
#!/usr/bin/env python
"""
Integration (not unit) tests for pylast.py
"""
from __future__ import annotations
import pylast
from .test_pylast import TestPyLastWithLastFm
class TestPyLastCountry(TestPyLastWithLastFm):
def test_country_is_hashable(self) -> None:
# Arrange
country = self.network.get_country("Italy")
# Act/Assert
self.helper_is_thing_hashable(country)
def test_countries(self) -> None:
# Arrange
country1 = pylast.Country("Italy", self.network)
country2 = pylast.Country("Finland", self.network)
# Act
text = str(country1)
rep = repr(country1)
url = country1.get_url()
# Assert
assert "Italy" in rep
assert "pylast.Country" in rep
assert text == "Italy"
assert country1 == country1
assert country1 != country2
assert url == "https://www.last.fm/place/italy"

56
tests/test_library.py Executable file
View file

@ -0,0 +1,56 @@
#!/usr/bin/env python
"""
Integration (not unit) tests for pylast.py
"""
from __future__ import annotations
import pylast
from .test_pylast import TestPyLastWithLastFm
class TestPyLastLibrary(TestPyLastWithLastFm):
def test_repr(self) -> None:
# Arrange
library = pylast.Library(user=self.username, network=self.network)
# Act
representation = repr(library)
# Assert
assert representation.startswith("pylast.Library(")
def test_str(self) -> None:
# Arrange
library = pylast.Library(user=self.username, network=self.network)
# Act
string = str(library)
# Assert
assert string.endswith("'s Library")
def test_library_is_hashable(self) -> None:
# Arrange
library = pylast.Library(user=self.username, network=self.network)
# Act/Assert
self.helper_is_thing_hashable(library)
def test_cacheable_library(self) -> None:
# Arrange
library = pylast.Library(self.username, self.network)
# Act/Assert
self.helper_validate_cacheable(library, "get_artists")
def test_get_user(self) -> None:
# Arrange
library = pylast.Library(user=self.username, network=self.network)
user_to_get = self.network.get_user(self.username)
# Act
library_user = library.get_user()
# Assert
assert library_user == user_to_get

43
tests/test_librefm.py Executable file
View file

@ -0,0 +1,43 @@
#!/usr/bin/env python
"""
Integration (not unit) tests for pylast.py
"""
from __future__ import annotations
from flaky import flaky
import pylast
from .test_pylast import load_secrets
@flaky(max_runs=3, min_passes=1)
class TestPyLastWithLibreFm:
"""Own class for Libre.fm because we don't need the Last.fm setUp"""
def test_libre_fm(self) -> None:
# Arrange
secrets = load_secrets()
username = secrets["username"]
password_hash = secrets["password_hash"]
# Act
network = pylast.LibreFMNetwork(password_hash=password_hash, username=username)
artist = network.get_artist("Radiohead")
name = artist.get_name()
# Assert
assert name == "Radiohead"
def test_repr(self) -> None:
# Arrange
secrets = load_secrets()
username = secrets["username"]
password_hash = secrets["password_hash"]
network = pylast.LibreFMNetwork(password_hash=password_hash, username=username)
# Act
representation = repr(network)
# Assert
assert representation.startswith("pylast.LibreFMNetwork(")

420
tests/test_network.py Executable file
View file

@ -0,0 +1,420 @@
"""
Integration (not unit) tests for pylast.py
"""
from __future__ import annotations
import re
import time
import pytest
import pylast
from .test_pylast import WRITE_TEST, TestPyLastWithLastFm
class TestPyLastNetwork(TestPyLastWithLastFm):
@pytest.mark.skipif(not WRITE_TEST, reason="Only test once to avoid collisions")
def test_scrobble(self) -> None:
# Arrange
artist = "test artist"
title = "test title"
timestamp = self.unix_timestamp()
lastfm_user = self.network.get_user(self.username)
# Act
self.network.scrobble(artist=artist, title="test title 2", timestamp=timestamp)
self.network.scrobble(artist=artist, title=title, timestamp=timestamp)
# Assert
# limit=2 to ignore now-playing:
last_scrobble = list(lastfm_user.get_recent_tracks(limit=2))[0]
assert str(last_scrobble.track.artist).lower() == artist
assert str(last_scrobble.track.title).lower() == title
@pytest.mark.skipif(not WRITE_TEST, reason="Only test once to avoid collisions")
def test_update_now_playing(self) -> None:
# Arrange
artist = "Test Artist"
title = "test title"
album = "Test Album"
track_number = 1
lastfm_user = self.network.get_user(self.username)
# Act
self.network.update_now_playing(
artist=artist, title=title, album=album, track_number=track_number
)
# Assert
current_track = lastfm_user.get_now_playing()
assert current_track is not None
assert str(current_track.title).lower() == "test title"
assert str(current_track.artist).lower() == "test artist"
assert current_track.info["album"] == "Test Album"
assert current_track.get_album().title == "Test Album"
assert len(current_track.info["image"])
assert re.search(r"^http.+$", current_track.info["image"][pylast.SIZE_LARGE])
def test_enable_rate_limiting(self) -> None:
# Arrange
assert not self.network.is_rate_limited()
# Act
self.network.enable_rate_limit()
then = time.time()
# Make some network call, limit not applied first time
self.network.get_top_artists()
# Make a second network call, limiting should be applied
self.network.get_top_artists()
now = time.time()
# Assert
assert self.network.is_rate_limited()
assert now - then >= 0.2
def test_disable_rate_limiting(self) -> None:
# Arrange
self.network.enable_rate_limit()
assert self.network.is_rate_limited()
# Act
self.network.disable_rate_limit()
# Make some network call, limit not applied first time
self.network.get_user(self.username)
# Make a second network call, limiting should be applied
self.network.get_top_artists()
# Assert
assert not self.network.is_rate_limited()
def test_lastfm_network_name(self) -> None:
# Act
name = str(self.network)
# Assert
assert name == "Last.fm Network"
def test_geo_get_top_artists(self) -> None:
# Arrange
# Act
artists = self.network.get_geo_top_artists(country="United Kingdom", limit=1)
# Assert
assert len(artists) == 1
assert isinstance(artists[0], pylast.TopItem)
assert isinstance(artists[0].item, pylast.Artist)
def test_geo_get_top_tracks(self) -> None:
# Arrange
# Act
tracks = self.network.get_geo_top_tracks(
country="United Kingdom", location="Manchester", limit=1
)
# Assert
assert len(tracks) == 1
assert isinstance(tracks[0], pylast.TopItem)
assert isinstance(tracks[0].item, pylast.Track)
def test_network_get_top_artists_with_limit(self) -> None:
# Arrange
# Act
artists = self.network.get_top_artists(limit=1)
# Assert
self.helper_only_one_thing_in_top_list(artists, pylast.Artist)
def test_network_get_top_tags_with_limit(self) -> None:
# Arrange
# Act
tags = self.network.get_top_tags(limit=1)
# Assert
self.helper_only_one_thing_in_top_list(tags, pylast.Tag)
def test_network_get_top_tags_with_no_limit(self) -> None:
# Arrange
# Act
tags = self.network.get_top_tags()
# Assert
self.helper_at_least_one_thing_in_top_list(tags, pylast.Tag)
def test_network_get_top_tracks_with_limit(self) -> None:
# Arrange
# Act
tracks = self.network.get_top_tracks(limit=1)
# Assert
self.helper_only_one_thing_in_top_list(tracks, pylast.Track)
def test_country_top_tracks(self) -> None:
# Arrange
country = self.network.get_country("Croatia")
# Act
things = country.get_top_tracks(limit=2)
# Assert
self.helper_two_different_things_in_top_list(things, pylast.Track)
def test_country_network_top_tracks(self) -> None:
# Arrange
# Act
things = self.network.get_geo_top_tracks("Croatia", limit=2)
# Assert
self.helper_two_different_things_in_top_list(things, pylast.Track)
def test_tag_top_tracks(self) -> None:
# Arrange
tag = self.network.get_tag("blues")
# Act
things = tag.get_top_tracks(limit=2)
# Assert
self.helper_two_different_things_in_top_list(things, pylast.Track)
def test_album_data(self) -> None:
# Arrange
thing = self.network.get_album("Test Artist", "Test Album")
# Act
stringed = str(thing)
rep = thing.__repr__()
title = thing.get_title()
name = thing.get_name()
playcount = thing.get_playcount()
url = thing.get_url()
# Assert
assert stringed == "Test Artist - Test Album"
assert "pylast.Album('Test Artist', 'Test Album'," in rep
assert title == name
assert isinstance(playcount, int)
assert playcount > 1
assert "https://www.last.fm/music/test%2bartist/test%2balbum" == url
def test_track_data(self) -> None:
# Arrange
thing = self.network.get_track("Test Artist", "test title")
# Act
stringed = str(thing)
rep = thing.__repr__()
title = thing.get_title()
name = thing.get_name()
playcount = thing.get_playcount()
url = thing.get_url(pylast.DOMAIN_FRENCH)
# Assert
assert stringed == "Test Artist - test title"
assert "pylast.Track('Test Artist', 'test title'," in rep
assert title == "test title"
assert title == name
assert isinstance(playcount, int)
assert playcount > 1
assert "https://www.last.fm/fr/music/test%2bartist/_/test%2btitle" == url
def test_country_top_artists(self) -> None:
# Arrange
country = self.network.get_country("Ukraine")
# Act
artists = country.get_top_artists(limit=1)
# Assert
self.helper_only_one_thing_in_top_list(artists, pylast.Artist)
def test_caching(self) -> None:
# Arrange
user = self.network.get_user("RJ")
# Act
self.network.enable_caching()
tags1 = user.get_top_tags(limit=1, cacheable=True)
tags2 = user.get_top_tags(limit=1, cacheable=True)
# Assert
assert self.network.is_caching_enabled()
assert tags1 == tags2
self.network.disable_caching()
assert not self.network.is_caching_enabled()
def test_album_mbid(self) -> None:
# Arrange
mbid = "03c91c40-49a6-44a7-90e7-a700edf97a62"
# Act
album = self.network.get_album_by_mbid(mbid)
album_mbid = album.get_mbid()
# Assert
assert isinstance(album, pylast.Album)
assert album.title == "Believe"
assert album_mbid == mbid
def test_artist_mbid(self) -> None:
# Arrange
mbid = "7e84f845-ac16-41fe-9ff8-df12eb32af55"
# Act
artist = self.network.get_artist_by_mbid(mbid)
# Assert
assert isinstance(artist, pylast.Artist)
assert artist.name in ("MusicBrainz Test Artist", "MusicBrainzz Test Artist")
def test_track_mbid(self) -> None:
# Arrange
mbid = "ebc037b1-cc9c-44f2-a21f-83c219f0e1e0"
# Act
track = self.network.get_track_by_mbid(mbid)
track_mbid = track.get_mbid()
# Assert
assert isinstance(track, pylast.Track)
assert track.title == "first"
assert track_mbid == mbid
def test_init_with_token(self) -> None:
# Arrange/Act
msg = None
try:
pylast.LastFMNetwork(
api_key=self.__class__.secrets["api_key"],
api_secret=self.__class__.secrets["api_secret"],
token="invalid",
)
except pylast.WSError as exc:
msg = str(exc)
# Assert
assert msg == "Unauthorized Token - This token has not been issued"
def test_proxy(self) -> None:
# Arrange
proxy = "http://example.com:1234"
# Act / Assert
self.network.enable_proxy(proxy)
assert self.network.is_proxy_enabled()
assert self.network.proxy == "http://example.com:1234"
self.network.disable_proxy()
assert not self.network.is_proxy_enabled()
def test_album_search(self) -> None:
# Arrange
album = "Nevermind"
# Act
search = self.network.search_for_album(album)
results = search.get_next_page()
# Assert
assert isinstance(results, list)
assert isinstance(results[0], pylast.Album)
def test_album_search_images(self) -> None:
# Arrange
album = "Nevermind"
search = self.network.search_for_album(album)
# Act
results = search.get_next_page()
images = results[0].info["image"]
# Assert
assert len(images) == 4
assert images[pylast.SIZE_SMALL].startswith("https://")
assert images[pylast.SIZE_SMALL].endswith(".png")
assert "/34s/" in images[pylast.SIZE_SMALL]
assert images[pylast.SIZE_EXTRA_LARGE].startswith("https://")
assert images[pylast.SIZE_EXTRA_LARGE].endswith(".png")
assert "/300x300/" in images[pylast.SIZE_EXTRA_LARGE]
def test_artist_search(self) -> None:
# Arrange
artist = "Nirvana"
# Act
search = self.network.search_for_artist(artist)
results = search.get_next_page()
# Assert
assert isinstance(results, list)
assert isinstance(results[0], pylast.Artist)
def test_artist_search_images(self) -> None:
# Arrange
artist = "Nirvana"
search = self.network.search_for_artist(artist)
# Act
results = search.get_next_page()
images = results[0].info["image"]
# Assert
assert len(images) == 5
assert images[pylast.SIZE_SMALL].startswith("https://")
assert images[pylast.SIZE_SMALL].endswith(".png")
assert "/34s/" in images[pylast.SIZE_SMALL]
assert images[pylast.SIZE_EXTRA_LARGE].startswith("https://")
assert images[pylast.SIZE_EXTRA_LARGE].endswith(".png")
assert "/300x300/" in images[pylast.SIZE_EXTRA_LARGE]
def test_track_search(self) -> None:
# Arrange
artist = "Nirvana"
track = "Smells Like Teen Spirit"
# Act
search = self.network.search_for_track(artist, track)
results = search.get_next_page()
# Assert
assert isinstance(results, list)
assert isinstance(results[0], pylast.Track)
def test_track_search_images(self) -> None:
# Arrange
artist = "Nirvana"
track = "Smells Like Teen Spirit"
search = self.network.search_for_track(artist, track)
# Act
results = search.get_next_page()
images = results[0].info["image"]
# Assert
assert len(images) == 4
assert images[pylast.SIZE_SMALL].startswith("https://")
assert images[pylast.SIZE_SMALL].endswith(".png")
assert "/34s/" in images[pylast.SIZE_SMALL]
assert images[pylast.SIZE_EXTRA_LARGE].startswith("https://")
assert images[pylast.SIZE_EXTRA_LARGE].endswith(".png")
assert "/300x300/" in images[pylast.SIZE_EXTRA_LARGE]
def test_search_get_total_result_count(self) -> None:
# Arrange
artist = "Nirvana"
track = "Smells Like Teen Spirit"
search = self.network.search_for_track(artist, track)
# Act
total = search.get_total_result_count()
# Assert
assert int(total) > 10000

File diff suppressed because it is too large Load diff

58
tests/test_tag.py Executable file
View file

@ -0,0 +1,58 @@
#!/usr/bin/env python
"""
Integration (not unit) tests for pylast.py
"""
from __future__ import annotations
import pylast
from .test_pylast import TestPyLastWithLastFm
class TestPyLastTag(TestPyLastWithLastFm):
def test_tag_is_hashable(self) -> None:
# Arrange
tag = self.network.get_top_tags(limit=1)[0]
# Act/Assert
self.helper_is_thing_hashable(tag)
def test_tag_top_artists(self) -> None:
# Arrange
tag = self.network.get_tag("blues")
# Act
artists = tag.get_top_artists(limit=1)
# Assert
self.helper_only_one_thing_in_top_list(artists, pylast.Artist)
def test_tag_top_albums(self) -> None:
# Arrange
tag = self.network.get_tag("blues")
# Act
albums = tag.get_top_albums(limit=1)
# Assert
self.helper_only_one_thing_in_top_list(albums, pylast.Album)
def test_tags(self) -> None:
# Arrange
tag1 = self.network.get_tag("blues")
tag2 = self.network.get_tag("rock")
# Act
tag_repr = repr(tag1)
tag_str = str(tag1)
name = tag1.get_name(properly_capitalized=True)
url = tag1.get_url()
# Assert
assert "blues" == tag_str
assert "pylast.Tag" in tag_repr
assert "blues" in tag_repr
assert "blues" == name
assert tag1 == tag1
assert tag1 != tag2
assert url == "https://www.last.fm/tag/blues"

231
tests/test_track.py Executable file
View file

@ -0,0 +1,231 @@
"""
Integration (not unit) tests for pylast.py
"""
from __future__ import annotations
import time
import pytest
import pylast
from .test_pylast import WRITE_TEST, TestPyLastWithLastFm
class TestPyLastTrack(TestPyLastWithLastFm):
@pytest.mark.skipif(not WRITE_TEST, reason="Only test once to avoid collisions")
def test_love(self) -> None:
# Arrange
artist = "Test Artist"
title = "test title"
track = self.network.get_track(artist, title)
lastfm_user = self.network.get_user(self.username)
# Act
track.love()
# Assert
loved = list(lastfm_user.get_loved_tracks(limit=1))
assert str(loved[0].track.artist).lower() == "test artist"
assert str(loved[0].track.title).lower() == "test title"
@pytest.mark.skipif(not WRITE_TEST, reason="Only test once to avoid collisions")
def test_unlove(self) -> None:
# Arrange
artist = pylast.Artist("Test Artist", self.network)
title = "test title"
track = pylast.Track(artist, title, self.network)
lastfm_user = self.network.get_user(self.username)
track.love()
# Act
track.unlove()
time.sleep(1) # Delay, for Last.fm latency. TODO Can this be removed later?
# Assert
loved = list(lastfm_user.get_loved_tracks(limit=1))
if len(loved): # OK to be empty but if not:
assert str(loved[0].track.artist) != "Test Artist"
assert str(loved[0].track.title) != "test title"
def test_user_play_count_in_track_info(self) -> None:
# Arrange
artist = "Test Artist"
title = "test title"
track = pylast.Track(
artist=artist, title=title, network=self.network, username=self.username
)
# Act
count = track.get_userplaycount()
# Assert
assert count >= 0
def test_user_loved_in_track_info(self) -> None:
# Arrange
artist = "Test Artist"
title = "test title"
track = pylast.Track(
artist=artist, title=title, network=self.network, username=self.username
)
# Act
loved = track.get_userloved()
# Assert
assert loved is not None
assert isinstance(loved, bool)
assert not isinstance(loved, str)
def test_track_is_hashable(self) -> None:
# Arrange
artist = self.network.get_artist("Test Artist")
track = artist.get_top_tracks(stream=False)[0].item
assert isinstance(track, pylast.Track)
# Act/Assert
self.helper_is_thing_hashable(track)
def test_track_wiki_content(self) -> None:
# Arrange
track = pylast.Track("Test Artist", "test title", self.network)
# Act
wiki = track.get_wiki_content()
# Assert
assert wiki is not None
assert len(wiki) >= 1
def test_track_wiki_summary(self) -> None:
# Arrange
track = pylast.Track("Test Artist", "test title", self.network)
# Act
wiki = track.get_wiki_summary()
# Assert
assert wiki is not None
assert len(wiki) >= 1
def test_track_get_duration(self) -> None:
# Arrange
track = pylast.Track("Daft Punk", "Something About Us", self.network)
# Act
duration = track.get_duration()
# Assert
assert duration >= 100000
def test_track_get_album(self) -> None:
# Arrange
track = pylast.Track("Nirvana", "Lithium", self.network)
# Act
album = track.get_album()
# Assert
assert str(album) == "Nirvana - Nevermind"
def test_track_get_similar(self) -> None:
# Arrange
track = pylast.Track("Cher", "Believe", self.network)
# Act
similar = track.get_similar()
# Assert
found = any(str(track.item) == "Cher - Strong Enough" for track in similar)
assert found
def test_track_get_similar_limits(self) -> None:
# Arrange
track = pylast.Track("Cher", "Believe", self.network)
# Act/Assert
assert len(track.get_similar(limit=20)) == 20
assert len(track.get_similar(limit=10)) <= 10
assert len(track.get_similar(limit=None)) >= 23
assert len(track.get_similar(limit=0)) >= 23
def test_tracks_notequal(self) -> None:
# Arrange
track1 = pylast.Track("Test Artist", "test title", self.network)
track2 = pylast.Track("Test Artist", "Test Track", self.network)
# Act
# Assert
assert track1 != track2
def test_track_title_prop_caps(self) -> None:
# Arrange
track = pylast.Track("test artist", "test title", self.network)
# Act
title = track.get_title(properly_capitalized=True)
# Assert
assert title == "Test Title"
def test_track_listener_count(self) -> None:
# Arrange
track = pylast.Track("test artist", "test title", self.network)
# Act
count = track.get_listener_count()
# Assert
assert count > 21
def test_album_tracks(self) -> None:
# Arrange
album = pylast.Album("Test Artist", "Test", self.network)
# Act
tracks = album.get_tracks()
url = tracks[0].get_url()
# Assert
assert isinstance(tracks, list)
assert isinstance(tracks[0], pylast.Track)
assert len(tracks) == 1
assert url.startswith("https://www.last.fm/music/test")
def test_track_eq_none_is_false(self) -> None:
# Arrange
track1 = None
track2 = pylast.Track("Test Artist", "test title", self.network)
# Act / Assert
assert track1 != track2
def test_track_ne_none_is_true(self) -> None:
# Arrange
track1 = None
track2 = pylast.Track("Test Artist", "test title", self.network)
# Act / Assert
assert track1 != track2
def test_track_get_correction(self) -> None:
# Arrange
track = pylast.Track("Guns N' Roses", "mrbrownstone", self.network)
# Act
corrected_track_name = track.get_correction()
# Assert
assert corrected_track_name == "Mr. Brownstone"
def test_track_with_no_mbid(self) -> None:
# Arrange
track = pylast.Track("Static-X", "Set It Off", self.network)
# Act
mbid = track.get_mbid()
# Assert
assert mbid is None

496
tests/test_user.py Executable file
View file

@ -0,0 +1,496 @@
#!/usr/bin/env python
"""
Integration (not unit) tests for pylast.py
"""
from __future__ import annotations
import calendar
import datetime as dt
import inspect
import os
import re
import pytest
import pylast
from .test_pylast import TestPyLastWithLastFm
class TestPyLastUser(TestPyLastWithLastFm):
def test_repr(self) -> None:
# Arrange
user = self.network.get_user("RJ")
# Act
representation = repr(user)
# Assert
assert representation.startswith("pylast.User('RJ',")
def test_str(self) -> None:
# Arrange
user = self.network.get_user("RJ")
# Act
string = str(user)
# Assert
assert string == "RJ"
def test_equality(self) -> None:
# Arrange
user_1a = self.network.get_user("RJ")
user_1b = self.network.get_user("RJ")
user_2 = self.network.get_user("Test User")
not_a_user = self.network
# Act / Assert
assert user_1a == user_1b
assert user_1a != user_2
assert user_1a != not_a_user
def test_get_name(self) -> None:
# Arrange
user = self.network.get_user("RJ")
# Act
name = user.get_name(properly_capitalized=True)
# Assert
assert name == "RJ"
def test_get_user_registration(self) -> None:
# Arrange
user = self.network.get_user("RJ")
# Act
registered = user.get_registered()
# Assert
if int(registered):
# Last.fm API broken? Used to be yyyy-mm-dd not Unix timestamp
assert registered == "1037793040"
else: # pragma: no cover
# Old way
# Just check date because of timezones
assert "2002-11-20 " in registered
def test_get_user_unixtime_registration(self) -> None:
# Arrange
user = self.network.get_user("RJ")
# Act
unixtime_registered = user.get_unixtime_registered()
# Assert
# Just check date because of timezones
assert unixtime_registered == 1037793040
def test_get_countryless_user(self) -> None:
# Arrange
# Currently test_user has no country set:
lastfm_user = self.network.get_user("test_user")
# Act
country = lastfm_user.get_country()
# Assert
assert country is None
def test_user_get_country(self) -> None:
# Arrange
lastfm_user = self.network.get_user("RJ")
# Act
country = lastfm_user.get_country()
# Assert
assert str(country) == "United Kingdom"
def test_user_equals_none(self) -> None:
# Arrange
lastfm_user = self.network.get_user(self.username)
# Act
value = lastfm_user is None
# Assert
assert not value
def test_user_not_equal_to_none(self) -> None:
# Arrange
lastfm_user = self.network.get_user(self.username)
# Act
value = lastfm_user is not None
# Assert
assert value
def test_now_playing_user_with_no_scrobbles(self) -> None:
# Arrange
# Currently test-account has no scrobbles:
user = self.network.get_user("test-account")
# Act
current_track = user.get_now_playing()
# Assert
assert current_track is None
def test_love_limits(self) -> None:
# Arrange
# Currently test-account has at least 23 loved tracks:
user = self.network.get_user("test-user")
# Act/Assert
assert len(user.get_loved_tracks(limit=20)) == 20
assert len(user.get_loved_tracks(limit=100)) <= 100
assert len(user.get_loved_tracks(limit=None)) >= 23
assert len(user.get_loved_tracks(limit=0)) >= 23
def test_user_is_hashable(self) -> None:
# Arrange
user = self.network.get_user(self.username)
# Act/Assert
self.helper_is_thing_hashable(user)
# Commented out because (a) it'll take a long time and (b) it strangely
# fails due Last.fm's complaining of hitting the rate limit, even when
# limited to one call per second. The ToS allows 5 calls per second.
# def test_get_all_scrobbles(self):
# # Arrange
# lastfm_user = self.network.get_user("RJ")
# self.network.enable_rate_limit() # this is going to be slow...
#
# # Act
# tracks = lastfm_user.get_recent_tracks(limit=None)
#
# # Assert
# self.assertGreaterEqual(len(tracks), 0)
def test_pickle(self) -> None:
# Arrange
import pickle
lastfm_user = self.network.get_user(self.username)
filename = str(self.unix_timestamp()) + ".pkl"
# Act
with open(filename, "wb") as f:
pickle.dump(lastfm_user, f)
with open(filename, "rb") as f:
loaded_user = pickle.load(f)
os.remove(filename)
# Assert
assert lastfm_user == loaded_user
@pytest.mark.xfail
def test_cacheable_user(self) -> None:
# Arrange
lastfm_user = self.network.get_authenticated_user()
# Act/Assert
self.helper_validate_cacheable(lastfm_user, "get_friends")
# no cover whilst xfail:
self.helper_validate_cacheable( # pragma: no cover
lastfm_user, "get_loved_tracks"
)
self.helper_validate_cacheable( # pragma: no cover
lastfm_user, "get_recent_tracks"
)
def test_user_get_top_tags_with_limit(self) -> None:
# Arrange
user = self.network.get_user("RJ")
# Act
tags = user.get_top_tags(limit=1)
# Assert
self.helper_only_one_thing_in_top_list(tags, pylast.Tag)
def test_user_top_tracks(self) -> None:
# Arrange
lastfm_user = self.network.get_user("RJ")
# Act
things = lastfm_user.get_top_tracks(limit=2)
# Assert
self.helper_two_different_things_in_top_list(things, pylast.Track)
def helper_assert_chart(self, chart, expected_type) -> None:
# Assert
assert chart is not None
assert len(chart) > 0
assert isinstance(chart[0], pylast.TopItem)
assert isinstance(chart[0].item, expected_type)
def helper_get_assert_charts(self, thing, date) -> None:
# Arrange
album_chart, track_chart = None, None
(from_date, to_date) = date
# Act
artist_chart = thing.get_weekly_artist_charts(from_date, to_date)
if type(thing) is not pylast.Tag:
album_chart = thing.get_weekly_album_charts(from_date, to_date)
track_chart = thing.get_weekly_track_charts(from_date, to_date)
# Assert
self.helper_assert_chart(artist_chart, pylast.Artist)
if type(thing) is not pylast.Tag:
self.helper_assert_chart(album_chart, pylast.Album)
self.helper_assert_chart(track_chart, pylast.Track)
def helper_dates_valid(self, dates) -> None:
# Assert
assert len(dates) >= 1
assert isinstance(dates[0], tuple)
(start, end) = dates[0]
assert start < end
def test_user_charts(self) -> None:
# Arrange
lastfm_user = self.network.get_user("RJ")
dates = lastfm_user.get_weekly_chart_dates()
self.helper_dates_valid(dates)
# Act/Assert
self.helper_get_assert_charts(lastfm_user, dates[0])
def test_user_top_artists(self) -> None:
# Arrange
lastfm_user = self.network.get_user(self.username)
# Act
artists = lastfm_user.get_top_artists(limit=1)
# Assert
self.helper_only_one_thing_in_top_list(artists, pylast.Artist)
def test_user_top_albums(self) -> None:
# Arrange
user = self.network.get_user("RJ")
# Act
albums = user.get_top_albums(limit=1)
# Assert
self.helper_only_one_thing_in_top_list(albums, pylast.Album)
top_album = albums[0].item
assert len(top_album.info["image"])
assert re.search(r"^http.+$", top_album.info["image"][pylast.SIZE_LARGE])
def test_user_tagged_artists(self) -> None:
# Arrange
lastfm_user = self.network.get_user(self.username)
tags = ["artisttagola"]
artist = self.network.get_artist("Test Artist")
artist.add_tags(tags)
# Act
artists = lastfm_user.get_tagged_artists("artisttagola", limit=1)
# Assert
self.helper_only_one_thing_in_list(artists, pylast.Artist)
def test_user_tagged_albums(self) -> None:
# Arrange
lastfm_user = self.network.get_user(self.username)
tags = ["albumtagola"]
album = self.network.get_album("Test Artist", "Test Album")
album.add_tags(tags)
# Act
albums = lastfm_user.get_tagged_albums("albumtagola", limit=1)
# Assert
self.helper_only_one_thing_in_list(albums, pylast.Album)
def test_user_tagged_tracks(self) -> None:
# Arrange
lastfm_user = self.network.get_user(self.username)
tags = ["tracktagola"]
track = self.network.get_track("Test Artist", "test title")
track.add_tags(tags)
# Act
tracks = lastfm_user.get_tagged_tracks("tracktagola", limit=1)
# Assert
self.helper_only_one_thing_in_list(tracks, pylast.Track)
def test_user_subscriber(self) -> None:
# Arrange
subscriber = self.network.get_user("RJ")
non_subscriber = self.network.get_user("Test User")
# Act
subscriber_is_subscriber = subscriber.is_subscriber()
non_subscriber_is_subscriber = non_subscriber.is_subscriber()
# Assert
assert subscriber_is_subscriber
assert not non_subscriber_is_subscriber
def test_user_get_image(self) -> None:
# Arrange
user = self.network.get_user("RJ")
# Act
url = user.get_image()
# Assert
assert url.startswith("https://")
def test_user_get_library(self) -> None:
# Arrange
user = self.network.get_user(self.username)
# Act
library = user.get_library()
# Assert
assert isinstance(library, pylast.Library)
def test_get_recent_tracks_from_to(self) -> None:
# Arrange
lastfm_user = self.network.get_user("RJ")
start = dt.datetime(2011, 7, 21, 15, 10)
end = dt.datetime(2011, 7, 21, 15, 15)
utc_start = calendar.timegm(start.utctimetuple())
utc_end = calendar.timegm(end.utctimetuple())
# Act
tracks = lastfm_user.get_recent_tracks(time_from=utc_start, time_to=utc_end)
# Assert
assert len(tracks) == 1
assert str(tracks[0].track.artist) == "Johnny Cash"
assert str(tracks[0].track.title) == "Ring of Fire"
def test_get_recent_tracks_limit_none(self) -> None:
# Arrange
lastfm_user = self.network.get_user("bbc6music")
start = dt.datetime(2020, 2, 15, 15, 00)
end = dt.datetime(2020, 2, 15, 15, 40)
utc_start = calendar.timegm(start.utctimetuple())
utc_end = calendar.timegm(end.utctimetuple())
# Act
tracks = lastfm_user.get_recent_tracks(
time_from=utc_start, time_to=utc_end, limit=None
)
# Assert
assert len(tracks) == 11
assert str(tracks[0].track.artist) == "Seun Kuti & Egypt 80"
assert str(tracks[0].track.title) == "Struggles Sounds"
def test_get_recent_tracks_is_streamable(self) -> None:
# Arrange
lastfm_user = self.network.get_user("bbc6music")
start = dt.datetime(2020, 2, 15, 15, 00)
end = dt.datetime(2020, 2, 15, 15, 40)
utc_start = calendar.timegm(start.utctimetuple())
utc_end = calendar.timegm(end.utctimetuple())
# Act
tracks = lastfm_user.get_recent_tracks(
time_from=utc_start, time_to=utc_end, limit=None, stream=True
)
# Assert
assert inspect.isgenerator(tracks)
def test_get_playcount(self) -> None:
# Arrange
user = self.network.get_user("RJ")
# Act
playcount = user.get_playcount()
# Assert
assert playcount >= 128387
def test_get_image(self) -> None:
# Arrange
user = self.network.get_user("RJ")
# Act
image = user.get_image()
# Assert
assert image.startswith("https://")
assert image.endswith(".png")
def test_get_url(self) -> None:
# Arrange
user = self.network.get_user("RJ")
# Act
url = user.get_url()
# Assert
assert url == "https://www.last.fm/user/rj"
def test_get_weekly_artist_charts(self) -> None:
# Arrange
user = self.network.get_user("bbc6music")
# Act
charts = user.get_weekly_artist_charts()
artist, weight = charts[0]
# Assert
assert artist is not None
assert isinstance(artist.network, pylast.LastFMNetwork)
def test_get_weekly_track_charts(self) -> None:
# Arrange
user = self.network.get_user("bbc6music")
# Act
charts = user.get_weekly_track_charts()
track, weight = charts[0]
# Assert
assert track is not None
assert isinstance(track.network, pylast.LastFMNetwork)
def test_user_get_track_scrobbles(self) -> None:
# Arrange
artist = "France Gall"
title = "Laisse Tomber Les Filles"
user = self.network.get_user("bbc6music")
# Act
scrobbles = user.get_track_scrobbles(artist, title)
# Assert
assert len(scrobbles) > 0
assert str(scrobbles[0].track.artist) == "France Gall"
assert scrobbles[0].track.title == "Laisse Tomber Les Filles"
def test_cacheable_user_get_track_scrobbles(self) -> None:
# Arrange
artist = "France Gall"
title = "Laisse Tomber Les Filles"
user = self.network.get_user("bbc6music")
# Act
result1 = user.get_track_scrobbles(artist, title, cacheable=False)
result2 = list(user.get_track_scrobbles(artist, title, cacheable=True))
result3 = list(user.get_track_scrobbles(artist, title))
# Assert
self.helper_validate_results(result1, result2, result3)

View file

@ -1,29 +1,70 @@
# -*- coding: utf-8 -*-
import mock
from __future__ import annotations
from unittest import mock
import pytest
import six
import pylast
def mock_network():
return mock.Mock(
_get_ws_auth=mock.Mock(return_value=("", "", ""))
)
return mock.Mock(_get_ws_auth=mock.Mock(return_value=("", "", "")))
@pytest.mark.parametrize('artist', [
u'\xe9lafdasfdsafdsa', u'ééééééé',
pylast.Artist(u'B\xe9l', mock_network()),
'fdasfdsafsaf not unicode',
])
def test_get_cache_key(artist):
request = pylast._Request(mock_network(), 'some_method',
params={'artist': artist})
@pytest.mark.parametrize(
"artist",
[
"\xe9lafdasfdsafdsa",
"ééééééé",
pylast.Artist("B\xe9l", mock_network()),
"fdasfdsafsaf not unicode",
],
)
def test_get_cache_key(artist) -> None:
request = pylast._Request(mock_network(), "some_method", params={"artist": artist})
request._get_cache_key()
@pytest.mark.parametrize('obj', [pylast.Artist(u'B\xe9l', mock_network())])
def test_cast_and_hash(obj):
assert type(six.text_type(obj)) is six.text_type
@pytest.mark.parametrize("obj", [pylast.Artist("B\xe9l", mock_network())])
def test_cast_and_hash(obj) -> None:
assert isinstance(str(obj), str)
assert isinstance(hash(obj), int)
@pytest.mark.parametrize(
"test_input, expected",
[
(
# Plain text
'<album mbid="">test album name</album>',
'<album mbid="">test album name</album>',
),
(
# Contains Unicode ENQ Enquiry control character
'<album mbid="">test album \u0005name</album>',
'<album mbid="">test album name</album>',
),
],
)
def test__remove_invalid_xml_chars(test_input: str, expected: str) -> None:
assert pylast._remove_invalid_xml_chars(test_input) == expected
@pytest.mark.parametrize(
"test_input, expected",
[
(
# Plain text
'<album mbid="">test album name</album>',
'<?xml version="1.0" ?><album mbid="">test album name</album>',
),
(
# Contains Unicode ENQ Enquiry control character
'<album mbid="">test album \u0005name</album>',
'<?xml version="1.0" ?><album mbid="">test album name</album>',
),
],
)
def test__parse_response(test_input: str, expected: str) -> None:
doc = pylast._parse_response(test_input)
assert doc.toxml() == expected

68
tox.ini
View file

@ -1,44 +1,40 @@
[tox]
envlist = py27, py36, py35, py34, pypy, pypy3
recreate = False
requires =
tox>=4.2
env_list =
lint
py{py3, 313, 312, 311, 310, 39, 38}
[testenv]
setenv =
PYLAST_USERNAME={env:PYLAST_USERNAME:}
PYLAST_PASSWORD_HASH={env:PYLAST_PASSWORD_HASH:}
PYLAST_API_KEY={env:PYLAST_API_KEY:}
PYLAST_API_SECRET={env:PYLAST_API_SECRET:}
extras =
tests
pass_env =
FORCE_COLOR
PYLAST_API_KEY
PYLAST_API_SECRET
PYLAST_PASSWORD_HASH
PYLAST_USERNAME
commands =
{envpython} -m pytest -v -s -W all \
--cov pylast \
--cov tests \
--cov-report html \
--cov-report term-missing \
--cov-report xml \
--random-order \
{posargs}
[testenv:lint]
skip_install = true
deps =
pyyaml
pytest
mock
ipdb
pytest-cov
flaky
commands = py.test -v -s -W all --cov pylast --cov-report term-missing {posargs}
pre-commit
pass_env =
PRE_COMMIT_COLOR
commands =
pre-commit run --all-files --show-diff-on-failure
[testenv:venv]
deps = ipdb
commands = {posargs}
[testenv:py2lint]
deps =
pep8
pyflakes
clonedigger
ipdb
commands =
pyflakes pylast
pyflakes tests
pep8 pylast
pep8 tests
./clonedigger.sh
[testenv:py3lint]
deps =
pep8
pyflakes
commands =
pyflakes pylast
pyflakes tests
pep8 pylast
pep8 tests
{posargs}