diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index ed8f4a432bc..234b07e766c 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -6,4 +6,4 @@ updates:
- package-ecosystem: 'github-actions'
directory: '/'
schedule:
- interval: 'daily'
+ interval: 'monthly'
diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml
index fe8ff089a67..c2a5519234d 100644
--- a/.github/workflows/ci-workflow.yml
+++ b/.github/workflows/ci-workflow.yml
@@ -6,20 +6,19 @@ name: Exercises check
on:
push:
branches:
- - master
- main
pull_request:
jobs:
housekeeping:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- name: Set up Python
- uses: actions/setup-python@v4
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405
with:
- python-version: 3.10.6
+ python-version: 3.13.5
- name: Download & Install dependencies
run: |
@@ -49,24 +48,20 @@ jobs:
./bin/template_status.py -v -p .problem-specifications
canonical_sync:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
needs: housekeeping
strategy:
matrix:
- python-version: [3.7, 3.8, 3.9, 3.10.6]
+ python-version: [3.10.6, 3.11.2, 3.12, 3.13.5]
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- - uses: actions/setup-python@v4
+ - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405
with:
python-version: ${{ matrix.python-version }}
- - name: Install dataclasses package
- if: ${{ matrix.python-version == '3.6' }}
- run: pip install dataclasses
-
- name: Install pytest
- run: pip install pytest~=7.1.2
+ run: pip install pytest~=8.4.0
- name: Check exercises
run: |
diff --git a/.github/workflows/issue-commenter.yml b/.github/workflows/issue-commenter.yml
index 7bddcd077c0..9f7631e1465 100644
--- a/.github/workflows/issue-commenter.yml
+++ b/.github/workflows/issue-commenter.yml
@@ -5,20 +5,20 @@ on:
jobs:
comment-on-new-issue:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
name: Comments for every NEW issue.
steps:
- name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- name: Read issue-comment.md
id: issue-comment
- uses: juliangruber/read-file-action@v1
+ uses: juliangruber/read-file-action@271ff311a4947af354c6abcd696a306553b9ec18
with:
path: .github/issue-comment.md
- name: Base comment
- uses: jd-0001/gh-action-comment-on-new-issue@v2.0.3
+ uses: jd-0001/gh-action-comment-on-new-issue@c443e1151cc69b146fd6918cc983ec1bd27ab254
with:
message: "${{ steps.issue-comment.outputs.content }}"
ignore-label: ":anger: prickle"
diff --git a/.github/workflows/no-important-files-changed.yml b/.github/workflows/no-important-files-changed.yml
new file mode 100644
index 00000000000..812e9129668
--- /dev/null
+++ b/.github/workflows/no-important-files-changed.yml
@@ -0,0 +1,23 @@
+name: No important files changed
+
+on:
+ pull_request_target:
+ types: [opened]
+ branches: [main]
+ paths:
+ - "exercises/concept/**"
+ - "exercises/practice/**"
+ - "!exercises/*/*/.approaches/**"
+ - "!exercises/*/*/.articles/**"
+ - "!exercises/*/*/.docs/**"
+ - "!exercises/*/*/.meta/**"
+
+permissions:
+ pull-requests: write
+
+jobs:
+ check:
+ uses: exercism/github-actions/.github/workflows/check-no-important-files-changed.yml@main
+ with:
+ repository: ${{ github.event.pull_request.head.repo.owner.login }}/${{ github.event.pull_request.head.repo.name }}
+ ref: ${{ github.head_ref }}
diff --git a/.github/workflows/ping-cross-track-maintainers-team.yml b/.github/workflows/ping-cross-track-maintainers-team.yml
new file mode 100644
index 00000000000..b6ec9c5662f
--- /dev/null
+++ b/.github/workflows/ping-cross-track-maintainers-team.yml
@@ -0,0 +1,16 @@
+name: Ping cross-track maintainers team
+
+on:
+ pull_request_target:
+ types:
+ - opened
+
+permissions:
+ pull-requests: write
+
+jobs:
+ ping:
+ if: github.repository_owner == 'exercism' # Stops this job from running on forks
+ uses: exercism/github-actions/.github/workflows/ping-cross-track-maintainers-team.yml@main
+ secrets:
+ github_membership_token: ${{ secrets.COMMUNITY_CONTRIBUTIONS_WORKFLOW_TOKEN }}
diff --git a/.github/workflows/pr-commenter.yml b/.github/workflows/pr-commenter.yml
index 13c0b62d44c..a70deb6b890 100644
--- a/.github/workflows/pr-commenter.yml
+++ b/.github/workflows/pr-commenter.yml
@@ -4,9 +4,9 @@ on:
jobs:
pr-comment:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
steps:
- - uses: exercism/pr-commenter-action@v1.4.0
+ - uses: exercism/pr-commenter-action@f4a6aa5acc07742989788e70fd89cdc0980f0d1e
with:
github-token: "${{ github.token }}"
config-file: ".github/pr-commenter.yml"
\ No newline at end of file
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
index 4742a0db0a0..29b31a0401e 100644
--- a/.github/workflows/stale.yml
+++ b/.github/workflows/stale.yml
@@ -6,9 +6,9 @@ on:
jobs:
stale:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
steps:
- - uses: actions/stale@v7
+ - uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 21
diff --git a/.github/workflows/test-runner.yml b/.github/workflows/test-runner.yml
index ecfc2a10fc8..c9b0d21a713 100644
--- a/.github/workflows/test-runner.yml
+++ b/.github/workflows/test-runner.yml
@@ -8,8 +8,8 @@ on:
jobs:
test-runner:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- name: Run test-runner
- run: docker-compose run test-runner
+ run: docker compose run test-runner
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index df8e36761c1..3f7813de10a 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -90,4 +90,4 @@ This policy was initially adopted from the Front-end London Slack community and
A version history can be seen on [GitHub](https://github.com/exercism/website-copy/edit/main/pages/code_of_conduct.md).
_This policy is a "living" document, and subject to refinement and expansion in the future.
-This policy applies to the Exercism website, the Exercism GitHub organization, any other Exercism-related communication channels (e.g. Slack, Twitter, email) and any other Exercism entity or event._
+This policy applies to the Exercism website, the Exercism GitHub organization, any other Exercism-related communication channels (e.g. Discord, Forum, Twitter, email) and any other Exercism entity or event._
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 691ca9f92fc..6ff557f7087 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -4,38 +4,41 @@
Contributing
[](https://forum.exercism.org)
- [](https://exercism.org)
+ [](https://exercism.org)
[](https://exercism.org/blog/freeing-our-maintainers)
[](https://github.com/exercism/python/actions?query=workflow%3A%22Exercises+check%22)
-
-
-Hi. ๐๐ฝ ๐ **We are happy you are here.** ๐ ๐
-
-
-
-
+> [!IMPORTANT]
+>
We are not accepting community contributions at this time.
+>
+>
+>
+>
+> We love our community. We're grateful you are interested in improving the Python track.
+> But our maintainers are **not accepting community contributions at this time.**
+> If you would like to discuss possible future changes, please open a [thread on the forum](https://forum.exercism.org/).
+>
+> This [community blog post](https://exercism.org/blog/freeing-our-maintainers) contains more details.
+>
+>
+>
-We ๐ ๐ our community.
-**`But our maintainers are not accepting community contributions at this time.`**
-Please read this [community blog post](https://exercism.org/blog/freeing-our-maintainers) for details.
-
-
+Hi. ๐๐ฝ ๐ **We are happy you are here.** ๐ ๐
+
**`exercism/Python`** is one of many programming language tracks on [exercism(dot)org][exercism-website].
This repo holds all the instructions, tests, code, & support files for Python _exercises_ currently under development or implemented & available for students.
-๐ Track exercises support Python `3.7` - `3.10.6`.
+๐ Track exercises support Python `3.10` - `3.13.5`.
Exceptions to this support are noted where they occur.
- ๐ Track tooling (_test-runner, representer, analyzer, and Continuous Integration_) runs on Python `3.10.6`.
+๐ Track tooling (_test-runner, representer, analyzer, and Continuous Integration_) runs on Python `3.13.5`.
-Exercises are grouped into **concept** exercises which teach the [Python syllabus][python-syllabus], and **practice** exercises, which are unlocked by progressing in the syllabus tree ๐ด .
-Concept exercises are constrained to a small set of language or syntax features.
-Practice exercises are open-ended, and can be used to practice concepts learned, try out new techniques, and _play_.
-These two exercise groupings can be found in the track [config.json][config-json], and under the `python/exercises` directory.
+Exercises are grouped into **concept** exercises which teach the [Python syllabus][python-syllabus], and **practice** exercises, which are unlocked by progressing in the syllabus tree ๐ด .
+Concept exercises are constrained to a small set of language or syntax features.
+Practice exercises are open-ended, and can be used to practice concepts learned, try out new techniques, and _play_. These two exercise groupings can be found in the track [config.json][config-json], and under the `python/exercises` directory.
@@ -44,34 +47,38 @@ These two exercise groupings can be found in the track [config.json][config-json
It is not uncommon to discover typos, confusing directions, or incorrect implementations of certain tests or code examples. Or you might have a great suggestion for a hint to aid students ( ๐ ), see optimizations for exemplar or test code, find missing test cases to add, or want to correct factual and/or logical errors. Or maybe you have a great idea ๐ก for an exercise or feature ( ๐ ).
_Our track is always a work in progress!_ ๐๐
-While contributions are paused, we ask that you [`open a thread in our community forum`](https://forum.exercism.org) to let us know what you have found/suggest.
+While contributions are paused, we ask that you [**open a thread in our community forum**](https://forum.exercism.org) to let us know what you have found/suggest.
## ๐ง **Did you write a patch that fixes a bug?**
-**`Our maintainers are not accepting community contributions at this time.`**
-Please read this [community blog post](https://exercism.org/blog/freeing-our-maintainers) for details.
+Our maintainers are not accepting community contributions at this time.
+
+Until the pause on contributions ends, all PRs from the larger community will be **automatically closed** with a note.
+We ask that you [**open a thread in our community forum**](https://forum.exercism.org) to discuss any potential changes. Changes may or may not be approved, depending on the forum discussion.
-Once the pause ends, we will **happily** consider your PR.
-Until that time, all PRs from the larger community will be **automatically closed** with a note.
+Please read this [community blog post](https://exercism.org/blog/freeing-our-maintainers) for additional details.
+
-We're leaving the general contributing docs below for our long-term collaborators and maintainers.
+We're leaving the track contributing docs below for our long-term collaborators and maintainers.
+
+
+ Python Track Contributing Docs
In General
-- Maintainers are happy to review your work and help troubleshoot with you. ๐ ๐
+- Maintainers are happy to review your work and help troubleshoot with you. ๐ ๐ If you need help, comment in the Pull Request/issue. ๐๐ฝโโ๏ธ
+ - **Please wait at least 72 hours before pinging or `@`ing reviewers directly.**
- Requests are reviewed as soon as is practical/possible.
- - (โ ) Reviewers may be in a different timezone โ , or tied up ๐งถ with other tasks.
- - **Please wait at least 72 hours before pinging.**
-- If you need help, comment in the Pull Request/issue. ๐๐ฝโโ๏ธ
+ - (โ ) Keep in mind that reviewers may be in a different timezone โ , or tied up ๐งถ with other tasks.
- If you would like in-progress feedback/discussion, please mark your Pull Request as a **`[draft]`**
- Pull Requests should be focused around a single exercise, issue, or change.
- Pull Request titles and descriptions should make clear **what** has changed and **why**.
- - Please link ๐ to any related issues the PR addresses.
+ - Please link ๐ to any related forum discussions or issues the PR addresses.
- ๐ [ Open an issue ][open-an-issue]๐ and discuss it with ๐งฐ maintainers _**before**_:
- creating a Pull Request making significant or breaking changes.
- for changes across multiple exercises, even if they are typos or small.
@@ -171,7 +178,7 @@ Our documents use [Markdown][markdown-language], with certain [alterations][exer
- Favor `f-strings` for dynamic failure messages. Please make your error messages as relevant and human-readable as possible.
- We relate test cases to **task number** via a custom [PyTest Marker][pytestmark].
- These take the form of `@pytest.mark.task(taskno=)`. See [Guido's Gorgeous Lasagna][guidos-gorgeous-lasagna-testfile] for an example.
-- We prefer **test data files** when test inputs/ouputs are verbose.
+- We prefer **test data files** when test inputs/outputs are verbose.
- These should be named with `_data` or `_test_data` at the end of the filename, and saved alongside the test case file.
- See the [Cater-Waiter][cater-waiter] exercise directory for an example of this setup.
- **Test data files** need to be added under an `editor` key within [`config.json "files"`][exercise-config-json].
@@ -196,13 +203,13 @@ _We know it, and trust us, we are working on fixing it._ But if you see
-This track officially supports Python `3.7 - 3.10.6` for students completing exercises.
-The track `test runner`, `analyzer`, and `representer` run in docker on `python:3.10.6-slim`.
+This track officially supports Python `3.10 - 3.13.5` for students completing exercises.
+The track `test runner`, `analyzer`, and `representer` run in docker on `python:3.13.5-alpine3.22`.
Although the majority of test cases are written using `unittest.TestCase`,
-- All exercises should be written for compatibility with Python `3.7` - `3.10.6`.
-- Version backward _incompatibility_ (_e.g_ an exercise using features introduced in `3.8`, `3.9`, or `3.10`) should be clearly noted in any exercise hints, links, introductions or other notes.
+- All exercises should be written for compatibility with Python `3.10` - `3.13.5`.
+- Version backward _incompatibility_ (_e.g_ an exercise using features introduced in Python `3.10`+ that would not work in Python `3.10`) should be clearly noted in any exercise hints, links, introductions or other notes.
- Here is an example of how the Python documentation handles [version-tagged ๐ท ][version-tagged-language-features] feature introduction.
@@ -223,12 +230,12 @@ Although the majority of test cases are written using `unittest.TestCase`,
- For specifications, refer to [Concept Exercise Anatomy][concept-exercise-anatomy], or [Practice Exercise Anatomy][practice-exercise-anatomy] depending on which type of exercise you are contributing to.
-- **Practice exercise**, descriptions and instructions come from a centralized, cross-track [problem specifications][problem-specifications] repository.
+- **Practice exercise** descriptions and instructions come from a centralized, cross-track [problem specifications][problem-specifications] repository.
- Any updates or changes need to be proposed/approved in `problem-specifications` first.
- If Python-specific changes become necessary, they need to be appended to the canonical instructions by creating a `instructions.append.md` file in this (`exercism/Python`) repository.
-- Practice Exercise **Test Suits** for many practice exercises are similarly [auto-generated](#auto-generated-files) from data in [problem specifications][problem-specifications].
+- Practice Exercise **Test Suits** for most practice exercises are similarly [auto-generated](#auto-generated-files) from data in [problem specifications][problem-specifications].
- Any changes to them need to be proposed/discussed in the `problem-specifications` repository and approved by **3 track maintainers**, since changes could potentially affect many (_or all_) exercism language tracks.
- If Python-specific test changes become necessary, they can be appended to the exercise `tests.toml` file.
@@ -271,9 +278,9 @@ Although the majority of test cases are written using `unittest.TestCase`,
- [ ] `.meta/config.json` (**required**)
- [ ] `.meta/example.py` (**required**)
- [ ] `.meta/design.md` (_optional_)
- - [ ] `.meta/template.j2` (_template for generating tests from cannonical data_)
- - [ ] `.meta/tests.toml` (_tests configuration from cannonical data_)
- - [ ] `_test.py` (_**auto-generated from cannonical data**_)
+ - [ ] `.meta/template.j2` (_template for generating tests from canonical data_)
+ - [ ] `.meta/tests.toml` (_tests configuration from canonical data_)
+ - [ ] `_test.py` (_**auto-generated from canonical data**_)
- [ ] `.py` (**required**)
@@ -370,43 +377,32 @@ configlet generate --spec-path path/to/problem/specifications --
configlet generate --spec-path path/to/problem/specifications
```
+
+
+
[.flake8]: https://github.com/exercism/python/blob/main/.flake8
[.style.yapf]: https://github.com/exercism/python/blob/main/.style.yapf
[american-english]: https://github.com/exercism/docs/blob/main/building/markdown/style-guide.md
-[being-a-good-community-member]: https://github.com/exercism/docs/tree/main/community/good-member
[card-games-testfile]: https://github.com/exercism/python/blob/main/exercises/concept/card-games/lists_test.py
[cater-waiter]: https://github.com/exercism/python/tree/main/exercises/concept/cater-waiter
[concept-exercise-anatomy]: https://github.com/exercism/docs/blob/main/building/tracks/concept-exercises.md
-[concept-exercises]: https://github.com/exercism/docs/blob/main/building/tracks/concept-exercises.md
[config-json]: https://github.com/exercism/javascript/blob/main/config.json
-[configlet-general]: https://github.com/exercism/configlet
[configlet-lint]: https://github.com/exercism/configlet#configlet-lint
[configlet]: https://github.com/exercism/docs/blob/main/building/configlet/generating-documents.md
[distinguishing-test-iterations]: https://docs.python.org/3/library/unittest.html#distinguishing-test-iterations-using-subtests
[enumerate]: https://docs.python.org/3/library/functions.html#enumerate
[eol]: https://en.wikipedia.org/wiki/Newline
[exercise-config-json]: https://github.com/exercism/docs/blob/main/building/tracks/concept-exercises.md#full-example
-[exercise-presentation]: https://github.com/exercism/docs/blob/main/building/tracks/presentation.md
-[exercism-admins]: https://github.com/exercism/docs/blob/main/community/administrators.md
-[exercism-code-of-conduct]: https://exercism.org/docs/using/legal/code-of-conduct
-[exercism-concepts]: https://github.com/exercism/docs/blob/main/building/tracks/concepts.md
-[exercism-contributors]: https://github.com/exercism/docs/blob/main/community/contributors.md
[exercism-internal-linking]: https://github.com/exercism/docs/blob/main/building/markdown/internal-linking.md
[exercism-markdown-specification]: https://github.com/exercism/docs/blob/main/building/markdown/markdown.md
[exercism-markdown-widgets]: https://github.com/exercism/docs/blob/main/building/markdown/widgets.md
-[exercism-mentors]: https://github.com/exercism/docs/tree/main/mentoring
-[exercism-tasks]: https://exercism.org/docs/building/product/tasks
-[exercism-track-maintainers]: https://github.com/exercism/docs/blob/main/community/maintainers.md
-[exercism-track-structure]: https://github.com/exercism/docs/tree/main/building/tracks
[exercism-website]: https://exercism.org/
-[exercism-writing-style]: https://github.com/exercism/docs/blob/main/building/markdown/style-guide.md
[flake8-noqa]: https://flake8.pycqa.org/en/3.1.1/user/ignoring-errors.html#in-line-ignoring-errors
[flake8]: http://flake8.pycqa.org/
[google-coding-style]: https://google.github.io/styleguide/pyguide.html
[guidos-gorgeous-lasagna-testfile]: https://github.com/exercism/python/blob/main/exercises/concept/guidos-gorgeous-lasagna/lasagna_test.py
-[help-wanted]: https://github.com/exercism/python/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22
[implicit-line-joining]: https://google.github.io/styleguide/pyguide.html#32-line-length
[markdown-language]: https://guides.github.com/pdfs/markdown-cheatsheet-online.pdf
[open-an-issue]: https://github.com/exercism/python/issues/new/choose
@@ -428,5 +424,4 @@ configlet generate --spec-path path/to/problem/specifications
[the-words-that-we-use]: https://github.com/exercism/docs/blob/main/community/good-member/words.md
[unittest]: https://docs.python.org/3/library/unittest.html#unittest.TestCase
[version-tagged-language-features]: https://docs.python.org/3/library/stdtypes.html#dict.popitem
-[website-contributing-section]: https://exercism.org/docs/building
[yapf]: https://github.com/google/yapf
diff --git a/README.md b/README.md
index 64930816362..20c3bd1ce0c 100644
--- a/README.md
+++ b/README.md
@@ -4,24 +4,43 @@
Exercism Python Track
[](https://forum.exercism.org)
- [](https://exercism.org)
+ [](https://exercism.org)
[](https://exercism.org/blog/freeing-our-maintainers)
[](https://github.com/exercism/python/actions?query=workflow%3A%22Exercises+check%22)
-Hi. ๐๐ฝ ๐ **We are happy you are here.** ๐ ๐
+> [!IMPORTANT]
+>
We are not accepting community contributions at this time.
+>
+>
+>
+>
+> We love our community. We're grateful you are interested in improving the Python track.
+> But our maintainers are **not accepting community contributions at this time.**
+> If you would like to suggest a change / discuss an issue, please open a [thread on the forum](https://forum.exercism.org/).
+>
+> This [community blog post](https://exercism.org/blog/freeing-our-maintainers) contains more details.
+>
+>
+>
+Hi. ๐๐ฝ ๐ **We are happy you are here.** ๐ ๐
+
+
+
**`exercism/Python`** is one of many programming language tracks on [exercism(dot)org][exercism-website].
This repo holds all the instructions, tests, code, & support files for Python _exercises_ currently under development or implemented & available for students.
-๐ Track exercises support Python `3.7` - `3.10.6`.
+๐ Track exercises support Python `3.10` - `3.13.13`.
Exceptions to this support are noted where they occur.
- ๐ Track tooling (_test-runner, representer, analyzer, and Continuous Integration_) runs on Python `3.10.6`.
+๐ Track tooling (_test-runner, representer, analyzer, and Continuous Integration_) runs on Python `3.13.13`.
-Exercises are grouped into **concept** exercises which teach the [Python syllabus][python-syllabus], and **practice** exercises, which are unlocked by progressing in the syllabus tree ๐ด . Concept exercises are constrained to a small set of language or syntax features. Practice exercises are open-ended, and can be used to practice concepts learned, try out new techniques, and _play_. These two exercise groupings can be found in the track [config.json][config-json], and under the `python/exercises` directory.
+Exercises are grouped into **concept** exercises which teach the [Python syllabus][python-syllabus], and **practice** exercises, which are unlocked by progressing in the syllabus tree ๐ด .
+Concept exercises are constrained to a small set of language or syntax features.
+Practice exercises are open-ended, and can be used to practice concepts learned, try out new techniques, and _play_. These two exercise groupings can be found in the track [config.json][config-json], and under the `python/exercises` directory.
@@ -41,30 +60,31 @@ It might also be helpful to look at [Being a Good Community Member][being-a-good
-We ๐ ๐ our community.
-**`But our maintainers are not accepting community contributions at this time.`**
-Please read this [community blog post](https://exercism.org/blog/freeing-our-maintainers) for details.
+We ๐ ๐ our community.
+**But our maintainers are not accepting community contributions at this time.**
+Please read this [community blog post][freeing-maintainers] for details.
-
+
Here to suggest a new feature or new exercise?? **Hooray!** ๐
-We'd love if you did that via our [Exercism Community Forum](https://forum.exercism.org/). Please keep in mind [Chesterton's Fence][chestertons-fence].
-_Thoughtful suggestions will likely result faster & more enthusiastic responses from volunteers._
+We'd love if you did that via our [Community Forum](https://forum.exercism.org/).
+Please read [Suggesting Exercise Improvements][suggesting-improvements] & [Chesterton's Fence][chestertons-fence].
+_Thoughtful suggestions will likely result in faster & more enthusiastic responses from volunteers._
-
โจ ๐ฆ _**Want to jump directly into Exercism specifications & detail?**_
[Structure][exercism-track-structure] **|** [Tasks][exercism-tasks] **|** [Concepts][exercism-concepts] **|** [Concept Exercises][concept-exercises] **|** [Practice Exercises][practice-exercises] **|** [Presentation][exercise-presentation]
[Writing Style Guide][exercism-writing-style] **|** [Markdown Specification][exercism-markdown-specification] (_โจ version in [contributing][website-contributing-section] on exercism.org_)
-
+
+
## Python Software and Documentation
-**Copyright ยฉ 2001-2022 Python Software Foundation. All rights reserved.**
+**Copyright ยฉ 2001-2026 Python Software Foundation. All rights reserved.**
Python software and documentation are licensed under the [PSF License Agreement][psf-license].
@@ -94,10 +114,11 @@ This repository uses the [MIT License](/LICENSE).
[exercism-track-structure]: https://github.com/exercism/docs/tree/main/building/tracks
[exercism-website]: https://exercism.org/
[exercism-writing-style]: https://github.com/exercism/docs/blob/main/building/markdown/style-guide.md
+[freeing-maintainers]: https://exercism.org/blog/freeing-our-maintainers
[practice-exercises]: https://github.com/exercism/docs/blob/main/building/tracks/practice-exercises.md
-[prs]: https://github.com/exercism/docs/blob/main/community/good-member/pull-requests.md
[psf-license]: https://docs.python.org/3/license.html#psf-license
[python-syllabus]: https://exercism.org/tracks/python/concepts
+[suggesting-improvements]: https://github.com/exercism/docs/blob/main/community/good-member/suggesting-exercise-improvements.md
[the-words-that-we-use]: https://github.com/exercism/docs/blob/main/community/good-member/words.md
[website-contributing-section]: https://exercism.org/docs/building
[zero-clause-bsd]: https://docs.python.org/3/license.html#zero-clause-bsd-license-for-code-in-the-python-release-documentation
diff --git a/bin/data.py b/bin/data.py
index 54dec7612b3..7fce51b9087 100644
--- a/bin/data.py
+++ b/bin/data.py
@@ -4,9 +4,16 @@
from itertools import chain
import json
from pathlib import Path
-import tomli
from typing import List, Any, Dict, Type
+# Tomli was subsumed into Python 3.11.x, but was renamed to to tomllib.
+# This avoids ci failures for Python < 3.11.2.
+try:
+ import tomllib
+except ModuleNotFoundError:
+ import tomli as tomllib
+
+
def _custom_dataclass_init(self, *args, **kwargs):
# print(self.__class__.__name__, "__init__")
@@ -355,7 +362,7 @@ class TestsTOML:
@classmethod
def load(cls, toml_path: Path):
with toml_path.open("rb") as f:
- data = tomli.load(f)
+ data = tomllib.load(f)
return cls({uuid: TestCaseTOML(uuid, *opts) for
uuid, opts in
data.items() if
diff --git a/bin/fetch-configlet b/bin/fetch-configlet
index 4800e150849..6bef43ab722 100755
--- a/bin/fetch-configlet
+++ b/bin/fetch-configlet
@@ -24,10 +24,11 @@ get_download_url() {
local latest='https://api.github.com/repos/exercism/configlet/releases/latest'
local arch
case "$(uname -m)" in
- x86_64) arch='x86-64' ;;
- *686*) arch='i386' ;;
- *386*) arch='i386' ;;
- *) arch='x86-64' ;;
+ aarch64|arm64) arch='arm64' ;;
+ x86_64) arch='x86-64' ;;
+ *686*) arch='i386' ;;
+ *386*) arch='i386' ;;
+ *) arch='x86-64' ;;
esac
local suffix="${os}_${arch}.${ext}"
curl "${curlopts[@]}" --header 'Accept: application/vnd.github.v3+json' "${latest}" |
@@ -47,7 +48,7 @@ main() {
fi
local os
- case "$(uname)" in
+ case "$(uname -s)" in
Darwin*) os='macos' ;;
Linux*) os='linux' ;;
Windows*) os='windows' ;;
@@ -58,8 +59,8 @@ main() {
local ext
case "${os}" in
- windows*) ext='zip' ;;
- *) ext='tar.gz' ;;
+ windows) ext='zip' ;;
+ *) ext='tar.gz' ;;
esac
echo "Fetching configlet..." >&2
@@ -69,16 +70,16 @@ main() {
curl "${curlopts[@]}" --output "${output_path}" "${download_url}"
case "${ext}" in
- *zip) unzip "${output_path}" -d "${output_dir}" ;;
- *) tar xzf "${output_path}" -C "${output_dir}" ;;
+ zip) unzip "${output_path}" -d "${output_dir}" ;;
+ *) tar xzf "${output_path}" -C "${output_dir}" ;;
esac
rm -f "${output_path}"
local executable_ext
case "${os}" in
- windows*) executable_ext='.exe' ;;
- *) executable_ext='' ;;
+ windows) executable_ext='.exe' ;;
+ *) executable_ext='' ;;
esac
local configlet_path="${output_dir}/configlet${executable_ext}"
diff --git a/bin/generate_tests.py b/bin/generate_tests.py
index d0406aad5e2..2ad23a9b5f1 100755
--- a/bin/generate_tests.py
+++ b/bin/generate_tests.py
@@ -17,12 +17,13 @@
from githelp import Repo
_py = sys.version_info
-if _py.major < 3 or (_py.major == 3 and _py.minor < 6):
- print("Python version must be at least 3.6")
+if _py.major < 3 or (_py.major == 3 and _py.minor < 7):
+ print("Python version must be at least 3.7")
sys.exit(1)
import argparse
from datetime import datetime
+from datetime import timezone
import difflib
import filecmp
import importlib.util
@@ -34,11 +35,17 @@
from itertools import repeat
from string import punctuation, whitespace
from subprocess import check_call
-import tomli
from tempfile import NamedTemporaryFile
from textwrap import wrap
from typing import Any, Dict, List, NoReturn, Union
+# Tomli was subsumed into Python 3.11.x, but was renamed to to tomllib.
+# This avoids ci failures for Python < 3.11.2.
+try:
+ import tomllib
+except ModuleNotFoundError:
+ import tomli as tomllib
+
from jinja2 import Environment, FileSystemLoader, TemplateNotFound, UndefinedError
from dateutil.parser import parse
@@ -131,7 +138,7 @@ def parse_datetime(string: str, strip_module: bool = False) -> datetime:
]| # OR
o(?:[0-8]{1,3}) # an octal value
| # OR
- x(?:[0-9A-Fa-f]{2}) # a hexidecimal value
+ x(?:[0-9A-Fa-f]{2}) # a hexadecimal value
| # OR
N # a unicode char name composed of
\{ # an opening brace
@@ -197,6 +204,8 @@ def regex_find(s: str, find: str) -> List[Any]:
def regex_split(s: str, find: str) -> List[str]:
return re.split(find, s)
+def join_test_inputs(test_inputs: list) -> str:
+ return "\n".join(test_inputs)
def filter_test_cases(cases: List[TypeJSON], opts: TestsTOML) -> List[TypeJSON]:
"""
@@ -254,6 +263,19 @@ def format_file(path: Path) -> NoReturn:
def check_template(slug: str, tests_path: Path, tmpfile: Path):
+ """Generate a new test file and diff against existing file.
+
+ Note: The timestamp in each test file creates issues with
+ Python difflib, so it is skipped when being prepped
+ for diff.
+
+ You can see this "skipping" on lines 281 & 283.
+ However, this rather crude method creates
+ an empty "false positive" diff. This empty diff is
+ then skipped in lines 293 & 294, so that it can be
+ considered a pass..
+ """
+
try:
check_ok = True
if not tmpfile.is_file():
@@ -264,20 +286,25 @@ def check_template(slug: str, tests_path: Path, tmpfile: Path):
check_ok = False
if check_ok and not filecmp.cmp(tmpfile, tests_path):
with tests_path.open() as f:
- current_lines = f.readlines()
+ current_lines = f.readlines()[3:]
with tmpfile.open() as f:
- rendered_lines = f.readlines()
- diff = difflib.unified_diff(
+ rendered_lines = f.readlines()[3:]
+
+ diff = list(difflib.unified_diff(
current_lines,
rendered_lines,
fromfile=f"[current] {tests_path.name}",
tofile=f"[generated] {tmpfile.name}",
- )
- logger.debug(f"{slug}: ##### DIFF START #####")
- for line in diff:
- logger.debug(line.strip())
- logger.debug(f"{slug}: ##### DIFF END #####")
- check_ok = False
+ lineterm="\n",
+ ))
+ if not diff:
+ check_ok = True
+ else:
+ logger.debug(f"{slug}: ##### DIFF START #####")
+ for line in diff:
+ logger.debug(line.strip())
+ logger.debug(f"{slug}: ##### DIFF END #####")
+ check_ok = False
if not check_ok:
logger.error(
f"{slug}: check failed; tests must be regenerated with bin/generate_tests.py"
@@ -384,9 +411,11 @@ def generate(
env.filters["regex_replace"] = regex_replace
env.filters["regex_find"] = regex_find
env.filters["regex_split"] = regex_split
+ env.filters["join_test_inputs"] = join_test_inputs
env.filters["zip"] = zip
env.filters["parse_datetime"] = parse_datetime
env.filters["escape_invalid_escapes"] = escape_invalid_escapes
+ env.globals["current_date"] = datetime.now(tz=timezone.utc).date()
env.tests["error_case"] = error_case
result = True
for exercise in sorted(Path("exercises/practice").glob(exercise_glob)):
diff --git a/concepts/basics/about.md b/concepts/basics/about.md
index 929582357c8..6f932bfd16f 100644
--- a/concepts/basics/about.md
+++ b/concepts/basics/about.md
@@ -6,7 +6,8 @@ Imperative, declarative (e.g., functional), and object-oriented programming _sty
Python puts a strong emphasis on code readability and (_similar to Haskell_) uses [significant indentation][significant indentation] to denote function, method, and class definitions.
-Python was created by Guido van Rossum and first released in 1991. The [Python Software Foundation][psf] manages and directs resources for Python and CPython development and receives proposals for changes to the language from [members][psf membership] of the community via [Python Enhancement Proposals or PEPs][peps].
+Python was created by Guido van Rossum and first released in 1991.
+The [Python Software Foundation][psf] manages and directs resources for Python and CPython development and receives proposals for changes to the language from [members][psf membership] of the community via [Python Enhancement Proposals or PEPs][peps].
Complete documentation for the current release can be found at [docs.python.org][python docs].
@@ -18,8 +19,14 @@ Complete documentation for the current release can be found at [docs.python.org]
- [Python FAQs][python faqs]
- [Python Glossary of Terms][python glossary of terms]
+
+
+This first concept introduces 4 major Python language features:
+1. Name Assignment (_variables and constants_),
+2. Functions (_the `def` keyword and the `return` keyword_),
+3. Comments, and
+4. Docstrings.
-This concept introduces 4 major Python language features: Name Assignment (_variables and constants_), Functions (_and the return keyword_), Comments, and Docstrings.
~~~~exercism/note
@@ -32,16 +39,16 @@ On the Python track, [variables][variables] are always written in [`snake_case`]
[snake case]: https://en.wikipedia.org/wiki/Snake_case
+[the zen of python]: https://www.python.org/dev/peps/pep-0020/
[variables]: https://realpython.com/python-variables/
[what is pythonic]: https://blog.startifact.com/posts/older/what-is-pythonic.html
-[the zen of python]: https://www.python.org/dev/peps/pep-0020/
~~~~
## Name Assignment (Variables & Constants)
In Python, there are no keywords used in creating variables or constants.
-Instead, programmers can bind [_names_][facts-and-myths-about-python-names] (also called _variables__) to any type of object using the assignment `=` operator: ` = `.
+Instead, programmers can bind [_names_][facts-and-myths-about-python-names] (also called _variables_) to any type of object using the assignment `=` operator: ` = `.
A name can be reassigned (or re-bound) to different values (different object types) over its lifetime.
For example, `my_first_variable` can be re-assigned many times using `=`, and can refer to different object types with each re-assignment:
@@ -57,20 +64,21 @@ For example, `my_first_variable` can be re-assigned many times using `=`, and ca
>>> print(my_first_variable)
2
->>> my_first_variable = "Now, I'm a string." # You may re-bind a name to a different object type and value.
+>>> my_first_variable = "Now, I'm a string." # <--You may re-bind a name to a different object type and value.
>>> print(type(my_first_variable))
+>>> my_first_variable = 'You can call me "str".' # <--Strings can be declared using single or double quote marks.
>>> print(my_first_variable)
-"Now, I'm a string." # Strings can be declared using single or double quote marks.
+You can call me "str".
-import collections
->>> my_first_variable = collections.Counter([1,1,2,3,3,3,4,5,6,7]) # Now my_first_variable has been re-bound to a Counter object.
+>>> import collections
+>>> my_first_variable = collections.Counter([1,1,2,3,3,3,4,5,6,7]) # <--Now my_first_variable has been re-bound to a Counter object.
>>> print(type(my_first_variable))
>>> print(my_first_variable)
->>> Counter({3: 3, 1: 2, 2: 1, 4: 1, 5: 1, 6: 1, 7: 1})
+Counter({3: 3, 1: 2, 2: 1, 4: 1, 5: 1, 6: 1, 7: 1})
```
@@ -94,19 +102,19 @@ MY_FIRST_CONSTANT = "Some other value"
## Functions
-In Python, units of functionality are encapsulated in [_functions._][functions], which are themselves [objects][objects] (_it's [turtles all the way down][turtles all the way down]_).
+In Python, units of functionality are encapsulated in [_functions_][functions], which are themselves [objects][objects] (_it's [turtles all the way down][turtles all the way down]_).
Functions can be executed by themselves, passed as arguments to other functions, nested, or bound to a class.
When functions are bound to a [class][classes] name, they're referred to as [methods][method objects].
Related functions and classes (_with their methods_) can be grouped together in the same file or module, and imported in part or in whole for use in other programs.
The `def` keyword begins a [function definition][function definition].
-Each function can have zero or more formal [parameters][parameters] in `()` parenthesis, followed by a `:` colon.
-Statements for the _body_ of the function begin on the line following `def` and must be _indented in a block_.
+Each function can have zero or more formal [parameters][parameters] in `()` parentheses, followed by a `:` colon.
+Statements for the _body_ of the function begin on the line following `def` and must be _indented in a block_:
```python
-# The body of a function is indented by 2 spaces, & prints the sum of the numbers.
+# The body of a function is indented by 2 spaces & prints the sum of the numbers.
def add_two_numbers(number_one, number_two):
total = number_one + number_two
print(total)
@@ -118,7 +126,7 @@ def add_two_numbers(number_one, number_two):
# Inconsistent indentation in your code blocks will raise an error.
>>> def add_three_numbers_misformatted(number_one, number_two, number_three):
... result = number_one + number_two + number_three # This was indented by 4 spaces.
-... print(result) #this was only indented by 3 spaces
+... print(result) # <--This was only indented by 3 spaces.
...
...
File "", line 3
@@ -127,24 +135,69 @@ def add_two_numbers(number_one, number_two):
IndentationError: unindent does not match any outer indentation level
```
-Functions explicitly return a value or object via the [`return`][return] keyword.
-Functions that do not have an explicit `return` expression will _implicitly_ return [`None`][none].
+
+Functions _explicitly_ return a value or object via the [`return`][return] keyword:
+
```python
-# Function definition on first line.
+# Function definition on first line, explicit return used on final line.
def add_two_numbers(number_one, number_two):
- result = number_one + number_two
- return result # Returns the sum of the numbers.
+ return number_one + number_two
+
+# Calling the function in the Python shell returns the sum of the numbers.
>>> add_two_numbers(3, 4)
7
-# This function will return None.
+# Assigning the function call to a variable and printing
+# the variable will also return the value.
+>>> sum_with_return = add_two_numbers(5, 6)
+>>> print(sum_with_return)
+11
+```
+
+Functions that do not have an _explicit_ expression following a `return` will _implicitly_ return the [`None`][none] object.
+The details of `None` will be covered in a later concept.
+For the purposes of this exercise and explanation, `None` is a placeholder that represents nothing, or null:
+
+
+```python
+
+# This function will return `None`
+def square_a_number(number):
+ square = number * number
+ return # <-- note that this return is not followed by an expression
+
+# Calling the function in the Python shell appears
+# to not return anything at all.
+>>> square_a_number(2)
+>>>
+
+
+# Using print() with the function call shows that
+# the function is actually returning the **None** object.
+>>> print(square_a_number(2))
+None
+```
+
+Functions that omit `return` will also _implicitly_ return the [`None`][none] object.
+This means that if you do not use `return` in a function, Python will return the `None` object for you.
+
+```python
+
+# This function omits a return keyword altogether
def add_two_numbers(number_one, number_two):
result = number_one + number_two
+>>> add_two_numbers(5, 7)
>>> print(add_two_numbers(5, 7))
None
+
+# Assigning the function call to a variable and printing
+# the variable will also show None.
+>>> sum_without_return = add_two_numbers(5, 6)
+>>> print(sum_without_return)
+None
```
@@ -154,32 +207,41 @@ Functions are [_called_][calls] or invoked using their name followed by `()`.
Dot (`.`) notation is used for calling functions defined inside a class or module.
```python
->>> def number_to_the_power_of(number_one, number_two):
- return number_one ** number_two
+>>> def raise_to_power(number, power):
+... return number ** power
...
->>> number_to_the_power_of(3,3) # Invoking the function with the arguments 3 and 3.
+>>> raise_to_power(3,3) # Invoking the function with the arguments 3 and 3.
27
# A mis-match between the number of parameters and the number of arguments will raise an error.
->>> number_to_the_power_of(4,)
+>>> raise_to_power(4,)
...
Traceback (most recent call last):
File "", line 1, in
-TypeError: number_to_the_power_of() missing 1 required positional argument: 'number_two'
+TypeError: raise_to_power() missing 1 required positional argument: 'power'
# Calling methods or functions in classes and modules.
>>> start_text = "my silly sentence for examples."
->>> str.upper(start_text) # Calling the upper() method for the built-in str class.
-"MY SILLY SENTENCE FOR EXAMPLES."
+>>> str.upper(start_text) # <--Calling the upper() method from the built-in str class on start_text.
+'MY SILLY SENTENCE FOR EXAMPLES.'
-# Importing the math module
-import math
+# Because a string is an instance of the str class, methods can also be called on them "directly".
+>>> start_text = "my silly sentence for examples."
+>>> start_text.upper() # <--Calling the upper() method on start_text directly.
+'MY SILLY SENTENCE FOR EXAMPLES.'
+
+# Alternatively, we can skip the variable assignment (although this gets messy quick).
+>>> "my silly sentence for examples.".upper()
+'MY SILLY SENTENCE FOR EXAMPLES.'
->>> math.pow(2,4) # Calling the pow() function from the math module
->>> 16.0
+
+# Importing the math module
+>>> import math
+>>> math.pow(2,4) # <--Calling the pow() function from the math module.
+16.0
```
@@ -210,14 +272,18 @@ Docstrings are declared using triple double quotes (""") indented at the same le
```python
+# An example from PEP257 of a multi-line docstring
+# reformatted to use Google style non-type hinted docstrings.
+# Some additional details can be found in the Sphinx documentation:
+# https://www.sphinx-doc.org/en/master/usage/extensions/napoleon.html#getting-started
-# An example from PEP257 of a multi-line docstring.
def complex(real=0.0, imag=0.0):
"""Form a complex number.
- Keyword arguments:
- real -- the real part (default 0.0)
- imag -- the imaginary part (default 0.0)
+ Keyword Arguments:
+ real (float): The real part of the number (default 0.0)
+ imag (float): The imaginary part of the number (default 0.0)
+
"""
if imag == 0.0 and real == 0.0:
@@ -234,33 +300,40 @@ Testing and `doctest` will be covered in a later concept.
```python
-# An example on a user-defined function.
->>> def number_to_the_power_of(number_one, number_two):
- """Raise a number to an arbitrary power.
+# An example on a user-defined function using a Google style docstring.
+>>> def raise_to_power(number, power):
+ """Raise a number to an arbitrary power.
+
+ Parameters:
+ number (int): The base number.
+ power (int): The power to raise the base number to.
+
+ Returns:
+ int: The number raised to the specified power.
+
+ Takes a number and raises it to the specified power, returning the result.
- :param number_one: int the base number.
- :param number_two: int the power to raise the base number to.
- :return: int - number raised to power of second number
-
- Takes number_one and raises it to the power of number_two, returning the result.
- """
+ """
- return number_one ** number_two
+ return number ** power
...
# Calling the .__doc__ attribute of the function and printing the result.
->>> print(number_to_the_power_of.__doc__)
+>>> print(raise_to_power.__doc__)
Raise a number to an arbitrary power.
- :param number_one: int the base number.
- :param number_two: int the power to raise the base number to.
- :return: int - number raised to power of second number
+Parameters:
+ number (int): The base number.
+ power (int): The power to raise the base number to.
- Takes number_one and raises it to the power of number_two, returning the result.
+Returns:
+ int: The number raised to the specified power.
+Takes a number and raises it to the specified power, returning the result.
+...
-# Printing the __doc__ attribute for the built-in type: str.
+# Printing the __doc__ attribute of the built-in type: str.
>>> print(str.__doc__)
str(object='') -> str
str(bytes_or_buffer[, encoding[, errors]]) -> str
@@ -270,10 +343,11 @@ errors is specified, then the object must expose a data buffer
that will be decoded using the given encoding and error handler.
Otherwise, returns the result of object.__str__() (if defined)
or repr(object).
-encoding defaults to sys.getdefaultencoding().
+encoding defaults to 'utf-8'.
errors defaults to 'strict'.
```
+
[PEP257]: https://www.python.org/dev/peps/pep-0257/
[calls]: https://docs.python.org/3/reference/expressions.html#calls
[classes]: https://docs.python.org/3/reference/datamodel.html#classes
diff --git a/concepts/basics/introduction.md b/concepts/basics/introduction.md
index e48e6726515..cb61a0184ab 100644
--- a/concepts/basics/introduction.md
+++ b/concepts/basics/introduction.md
@@ -2,23 +2,31 @@
Python is a [dynamic and strongly typed][dynamic typing in python] programming language.
It employs both [duck typing][duck typing] and [gradual typing][gradual typing], via [type hints][type hints].
+Python puts a strong emphasis on code readability and (_similar to Haskell_) uses [significant indentation][significant indentation] to denote function, method, and class definitions.
+
+Python was created by Guido van Rossum and first released in 1991.
Imperative, declarative (e.g., functional), and object-oriented programming _styles_ are all supported, but internally **[everything in Python is an object][everythings an object]**.
-Python puts a strong emphasis on code readability and (_similar to Haskell_) uses [significant indentation][significant indentation] to denote function, method, and class definitions.
+We'll dig more into what all of that means as we continue through the Python track concepts.
-Python was created by Guido van Rossum and first released in 1991.
+This first concept (`basics`) introduces 4 major Python language features:
+1. Name Assignment (_variables and constants_),
+2. Functions (_the `def` keyword and the `return` keyword_),
+3. Comments, and
+4. Docstrings.
+
## Name Assignment (Variables & Constants)
-Programmers can bind [_names_][facts-and-myths-about-python-names] (also called _variables__) to any type of object using the assignment `=` operator: ` = `.
-A name can be reassigned (or re-bound) to different values (different object types) over its lifetime.
+Programmers can bind [_names_][facts-and-myths-about-python-names] (also called _variables_) to any type of object using the assignment `=` operator: ` = `.
+A name can be reassigned (or re-bound) to different values (different object types) over its lifetime:
```python
->>> my_first_variable = 1 # my_first_variable bound to an integer object of value one.
->>> my_first_variable = 2 # my_first_variable re-assigned to integer value 2.
+>>> my_first_variable = 1 # <--my_first_variable bound to an integer object of value one.
+>>> my_first_variable = 2 # <--my_first_variable re-assigned to integer value 2.
>>> print(type(my_first_variable))
@@ -26,31 +34,33 @@ A name can be reassigned (or re-bound) to different values (different object typ
>>> print(my_first_variable)
2
->>> my_first_variable = "Now, I'm a string." # You may re-bind a name to a different object type and value.
+>>> my_first_variable = "Now, I'm a string." # <--You may re-bind a name to a different object type and value.
>>> print(type(my_first_variable))
+>>> my_first_variable = 'You can call me "str".' # <--Strings can be declared using single or double quote marks.
>>> print(my_first_variable)
-"Now, I'm a string." # Strings can be declared using single or double quote marks.
+You can call me "str".
```
### Constants
-Constants are names meant to be assigned only once in a program.
-They should be defined at a [module][module] (file) level, and are typically visible to all functions and classes in the program.
-Using `SCREAMING_SNAKE_CASE` signals that the name should not be re-assigned, or its value mutated.
+Constants are names meant to be assigned only once in a program โ although Python will not prevent re-assignment.
+Using `SCREAMING_SNAKE_CASE` signals to anyone reading the code that the name should **not** be re-assigned, or its value mutated.
+Constants should be defined at a [module][module] (file) level, and are typically visible to all functions and classes in a program.
+
## Functions
The `def` keyword begins a [function definition][function definition].
-Each function can have zero or more formal [parameters][parameters] in `()` parenthesis, followed by a `:` colon.
+Each function can have zero or more formal [parameters][parameters] in `()` parentheses, followed by a `:` colon.
Statements for the _body_ of the function begin on the line following `def` and must be _indented in a block_.
```python
-# The body of a function is indented by 2 spaces, & prints the sum of the numbers.
+# The body of this function is indented by 2 spaces & prints the sum of the numbers.
def add_two_numbers(number_one, number_two):
total = number_one + number_two
print(total)
@@ -62,7 +72,7 @@ def add_two_numbers(number_one, number_two):
# Inconsistent indentation in your code blocks will raise an error.
>>> def add_three_numbers_misformatted(number_one, number_two, number_three):
... result = number_one + number_two + number_three # This was indented by 4 spaces.
-... print(result) #this was only indented by 3 spaces
+... print(result) # <--This was only indented by 3 spaces.
...
...
File "", line 3
@@ -71,24 +81,68 @@ def add_two_numbers(number_one, number_two):
IndentationError: unindent does not match any outer indentation level
```
-Functions explicitly return a value or object via the [`return`][return] keyword.
-Functions that do not have an explicit `return` expression will _implicitly_ return [`None`][none].
+
+Functions _explicitly_ return a value or object via the [`return`][return] keyword:
+
```python
-# Function definition on first line.
+# Function definition on first line, explicit return used on final line.
def add_two_numbers(number_one, number_two):
- result = number_one + number_two
- return result # Returns the sum of the numbers.
+ return number_one + number_two
+
+# Calling the function in the Python shell returns the sum of the numbers.
>>> add_two_numbers(3, 4)
7
-# This function will return None.
+# Assigning the function call to a variable and printing
+# the variable will also return the value.
+>>> sum_with_return = add_two_numbers(5, 6)
+>>> print(sum_with_return)
+11
+```
+
+
+Functions that do not have an _explicit_ expression following a `return` will _implicitly_ return the [`None`][none] object.
+The details of `None` will be covered in a later concept.
+For the purposes of this exercise and explanation, `None` is a placeholder that represents nothing, or null:
+
+
+```python
+# This function will return `None`
+def square_a_number(number):
+ square = number * number
+ return # <-- note that this return is not followed by an expression
+
+# Calling the function in the Python shell appears
+# to not return anything at all.
+>>> square_a_number(2)
+>>>
+
+
+# Using print() with the function call shows that
+# the function is actually returning the **None** object.
+>>> print(square_a_number(2))
+None
+```
+
+Functions that omit `return` will also _implicitly_ return the [`None`][none] object.
+This means that if you do not use `return` in a function, Python will return the `None` object for you.
+
+```python
+# This function omits a return keyword altogether.
def add_two_numbers(number_one, number_two):
result = number_one + number_two
+>>> add_two_numbers(5, 7)
>>> print(add_two_numbers(5, 7))
None
+
+# Assigning the function call to a variable and printing
+# the variable will also show None.
+>>> sum_without_return = add_two_numbers(5, 6)
+>>> print(sum_without_return)
+None
```
@@ -102,29 +156,35 @@ Each line of a comment block must start with the `#` character.
## Docstrings
The first statement of a function body can optionally be a [_docstring_][docstring], which concisely summarizes the function or object's purpose.
-Docstring conventions are laid out in [PEP257][pep257].
+Docstrings are read by automated documentation tools such as [Sphinx][sphinx] and are returned by calling the special attribute `.__doc__` on the function, method, or class name.
+General docstring conventions are laid out in [PEP257][pep257], but exact formats will vary by project and team.
Docstrings are declared using triple double quotes (""") indented at the same level as the code block:
```python
-
-# An example from PEP257 of a multi-line docstring.
+# An example from PEP257 of a multi-line docstring
+# reformatted to use Google style non-type hinted docstrings.
+# Some additional details can be found in the Sphinx documentation:
+# https://www.sphinx-doc.org/en/master/usage/extensions/napoleon.html#getting-started
def complex(real=0.0, imag=0.0):
"""Form a complex number.
- Keyword arguments:
- real -- the real part (default 0.0)
- imag -- the imaginary part (default 0.0)
+ Keyword Arguments:
+ real (float): The real part of the number (default 0.0)
+ imag (float): The imaginary part of the number (default 0.0)
+
"""
if imag == 0.0 and real == 0.0:
return complex_zero
-
```
-[pep257]: https://www.python.org/dev/peps/pep-0257/
+Docstrings can also function as [lightweight unit tests][doctests], which will be covered in a later concept.
+
+
[comments]: https://realpython.com/python-comments-guide/#python-commenting-basics
[docstring]: https://docs.python.org/3/tutorial/controlflow.html#tut-docstrings
+[doctests]: https://docs.python.org/3/library/doctest.html
[duck typing]: https://en.wikipedia.org/wiki/Duck_typing
[dynamic typing in python]: https://stackoverflow.com/questions/11328920/is-python-strongly-typed
[everythings an object]: https://docs.python.org/3/reference/datamodel.html
@@ -134,6 +194,8 @@ def complex(real=0.0, imag=0.0):
[module]: https://docs.python.org/3/tutorial/modules.html
[none]: https://docs.python.org/3/library/constants.html
[parameters]: https://docs.python.org/3/glossary.html#term-parameter
+[pep257]: https://www.python.org/dev/peps/pep-0257/
[return]: https://docs.python.org/3/reference/simple_stmts.html#return
-[type hints]: https://docs.python.org/3/library/typing.html
[significant indentation]: https://docs.python.org/3/reference/lexical_analysis.html#indentation
+[sphinx]: https://www.sphinx-doc.org/en/master/usage/index.html
+[type hints]: https://docs.python.org/3/library/typing.html
diff --git a/concepts/basics/links.json b/concepts/basics/links.json
index 3e5561228ee..1d1d640c9e7 100644
--- a/concepts/basics/links.json
+++ b/concepts/basics/links.json
@@ -1,5 +1,6 @@
[
- {"url": "https://lerner.co.il/2019/06/18/understanding-python-assignment/",
+ {
+ "url": "https://lerner.co.il/2019/06/18/understanding-python-assignment/",
"description": "Reuven Lerner: Understanding Python Assignment"
},
{
@@ -14,6 +15,10 @@
"url": "https://www.pythonmorsels.com/everything-is-an-object/",
"description": "Python Morsels: Everything is an Object"
},
+ {
+ "url": "https://eli.thegreenplace.net/2012/03/23/python-internals-how-callables-work/",
+ "description": "Eli Bendersky: Python internals: how callables work"
+ },
{
"url": "https://stackoverflow.com/questions/11328920/is-python-strongly-typed",
"description": "dynamic typing and strong typing"
diff --git a/concepts/binary-octal-hexadecimal/.meta/config.json b/concepts/binary-octal-hexadecimal/.meta/config.json
new file mode 100644
index 00000000000..6e2a15b607a
--- /dev/null
+++ b/concepts/binary-octal-hexadecimal/.meta/config.json
@@ -0,0 +1,4 @@
+{
+ "blurb": "Other numerical systems in Python: binary (0b11), octal (0o71), and hex (0xFF)",
+ "authors": ["BethanyG", "meatball133"]
+}
diff --git a/concepts/binary-octal-hexadecimal/about.md b/concepts/binary-octal-hexadecimal/about.md
new file mode 100644
index 00000000000..67646aed2f2
--- /dev/null
+++ b/concepts/binary-octal-hexadecimal/about.md
@@ -0,0 +1,226 @@
+# Binary, Octal, and Hexadecimal
+
+Binary, octal, and hexadecimal (_also known as hex_) are different [numeral systems][numeral-systems] with different bases.
+Binary is base 2, octal is base 8, and hexadecimal is base 16.
+Normal integers are base 10 in python.
+Binary, octal, and hexadecimal are all representations of integers.
+Which means that they represent positive and negative numbers (_including zero_) without fractions or decimals, and support all the operations that we can do with integers.
+
+## Binary
+
+[Binary][binary] is a base 2 numeral system, using only the digits 0 and 1.
+It commonly represents the 0 ("off") and 1 ("on") states of electrical flow through transistors and switches in computers, as well as the positive and negative charges in magnetic storage media.
+Binary can represent all the integers that are used in base 10.
+
+A snippet from the base 2 system looks like this, although it continues infinitely and doesn't stop at 128:
+
+| 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
+| -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- |
+| 2 \*\* 7 | 2 \*\* 6 | 2 \*\* 5 | 2 \*\* 4 | 2 \*\* 3 | 2 \*\* 2 | 2 \*\* 1 | 2 \*\* 0 |
+
+So if we want to represent the number 6 in binary, it would be 110.
+
+| Place value | 4 | 2 | 1 |
+| ------------- | --- | --- | --- |
+| Binary number | 1 | 1 | 0 |
+
+And the operation would be: `4 + 2 + 0 = 6`
+
+Another example: 19
+
+| Place value | 16 | 8 | 4 | 2 | 1 |
+| ------------- | --- | --- | --- | --- | --- |
+| Binary number | 1 | 0 | 0 | 1 | 1 |
+
+The binary number would be: 10011
+And the operation would be: `16 + 0 + 0 + 2 + 1 = 19`
+
+## Binary in Python
+
+In Python, we can represent binary literals using the `0b` prefix.
+If we write `0b10011`, Python will interpret it as a binary number and convert it to base 10.
+
+```python
+>>> 0b10011
+19
+
+>>> type(0b10011)
+
+```
+
+Binary in Python is just a different way of writing an integer and so the binary representation **is an integer** for all mathematical operations.
+
+If you write a number with a `0b` prefix that is not in the binary system, it will raise a `SyntaxError`.
+
+```python
+Traceback (most recent call last):
+ File "c:\binary.py", line 1, in
+ 0b10211
+SyntaxError: invalid digit '2' in binary literal
+```
+
+### Operations with Binary Numbers
+
+Since binary numbers are integers, we can perform all operations on them that we can with integers.
+
+```python
+# addition
+>>> 0b10011 + 0b10011
+38
+
+# multiplication
+>>> 0b10011 * 0b10011
+361
+```
+
+We can also perform operations between both binary and integer representations.
+However, the usual mathematical operator rules apply: dividing two binary numbers or integer numbers will return a `float`, even if the division does not result in a decimal portion.
+
+```python
+>>> 0b10011 + 19
+38
+
+>>> 0b10011/0b10011
+1.0
+
+>>> 0b10011/3
+6.333333333333333
+```
+
+
+### Converting to and from Binary Representation
+
+Python will automatically convert a binary literal into `int`.
+ To convert an `int` into a binary representation, use the built-in [`bin()`][bin] function.
+`bin()` will return a `str` of the binary equivalent with the prefix `0b` .
+
+```python
+>>> bin(19)
+'0b10011'
+```
+
+To convert a binary literal to an integer, we can use the built-in `int()` function, and pass a string of the binary representation and a base argument:
+
+```python
+>>> int("0b10011", 2)
+19
+```
+
+Giving the wrong base (_or an invalid binary representation_) will raise a `ValueError`:
+
+```python
+Traceback (most recent call last):
+ File "c:\binary.py", line 4, in
+ int("0b10011", 3)
+ValueError: invalid literal for int() with base 3: '0b10011'
+```
+
+### Binary Methods
+
+There are also some special [methods][numeral-systems] that we can use on binary numbers.
+
+
+[`.bit_length()`][bit_length] will return the number of bits that are needed to represent the number:
+
+```python
+>>> 0b11011.bit_length()
+5
+```
+
+
+[`.bit_count()`][bit_count] will return the number of **ones** in the binary number.
+For example, `bit_count()` on '0b11011' will return 4:
+
+```python
+>>> 0b11011.bit_count()
+4
+```
+
+
+~~~~exercism/note
+If you are working locally, `bit_count()` requires at least Python 3.10.
+The Exercism online editor currently supports all features through Python 3.11.
+~~~~
+
+
+## Octal
+
+[Octal][octal] is a base 8 numeral system.
+It uses the digits 0, 1, 2, 3, 4, 5, 6, and 7.
+
+In Python, we can represent octal numbers using the `0o` prefix.
+As with binary, Python automatically converts an octal representation to an `int`.
+
+```python
+>>> 0o123
+83
+```
+
+As with binary, octal numbers **are ints** and support all integer operations.
+Prefixing a number with `0o` that is not in the octal system will raise a `SyntaxError`.
+
+ ### Converting to and from Octal Representation
+
+To convert an `int` into an octal representation, you can use the built-in [`oct()`][oct] function.
+This acts similarly to the `bin()` function, returning a string:
+
+```python
+>>> oct(83)
+'0o123'
+```
+
+
+To convert an octal number to an integer, we can use the `int()` function, passing an octal string representation and the base (8) as arguments:
+
+```python
+>>> int("0o123", 8)
+83
+```
+
+As with binary, giving the wrong base will raise a `ValueError`.
+
+## Hexadecimal
+
+[Hexadecimal][hexadecimal] is a base 16 numeral system.
+It uses the digits 0 - 9 and the letters A, B, C, D, E, and F.
+A is 10, B is 11, C is 12, D is 13, E is 14, and F is 15.
+
+We can represent hexadecimal numbers in Python using the `0x` prefix.
+As with binary and octal, Python will automatically convert hexadecimal literals to `int`s.
+
+```python
+>>> 0x123
+291
+```
+
+As with binary and octal โ hexadecimal literals **are ints**, and you can perform all integer operations with them.
+Prefixing a non-hexadecimal number with `0x` will raise a `SyntaxError`.
+
+
+### Converting to and from Hexadecimal Representation
+
+To convert an `int` into a hexadecimal representation, you can use the built-in [`hex()`][hex] function.
+This acts similarly to the `bin()` function, returning a string:
+
+```python
+>>> hex(291)
+'0x123'
+```
+
+
+To convert a hexadecimal representation to an integer, we can use the `int()` function, passing a hexadecimal string with the base (16) as arguments:
+
+```python
+>>> int("0x123", 16)
+291
+```
+
+As with binary and octal, giving the wrong base will raise a `ValueError`.
+
+
+[binary]: https://en.wikipedia.org/wiki/Binary_number
+[bit_count]: https://docs.python.org/3/library/stdtypes.html#int.bit_count
+[bit_length]: https://docs.python.org/3/library/stdtypes.html#int.bit_length
+[hexadecimal]: https://en.wikipedia.org/wiki/Hexadecimal
+[numeral-systems]: https://en.wikipedia.org/wiki/Numeral_system
+[octal]: https://en.wikipedia.org/wiki/Octal
diff --git a/concepts/binary-octal-hexadecimal/introduction.md b/concepts/binary-octal-hexadecimal/introduction.md
new file mode 100644
index 00000000000..84ff634263d
--- /dev/null
+++ b/concepts/binary-octal-hexadecimal/introduction.md
@@ -0,0 +1,11 @@
+# Binary, Octal, Hexadecimal
+
+Binary, octal, and hexadecimal (_also known as hex_) are different [numeral systems][numeral-systems] with different bases.
+Binary is base 2, octal is base 8, and hexadecimal is base 16.
+Normal integers are base 10 in python.
+Binary, octal, and hexadecimal literals are all considered `int` subtypes and Python automatically converts between them.
+This means that they can only represent zero, positive, and negative numbers that do not have a fractional or decimal part.
+Binary, octal, and hexadecimal numbers support all integer operations.
+However, division (_as with ints_) will return a `float`.
+
+[numeral-systems]: https://en.wikipedia.org/wiki/Numeral_system
diff --git a/concepts/binary-octal-hexadecimal/links.json b/concepts/binary-octal-hexadecimal/links.json
new file mode 100644
index 00000000000..8826182cd48
--- /dev/null
+++ b/concepts/binary-octal-hexadecimal/links.json
@@ -0,0 +1,10 @@
+[
+ {
+ "url": "https://towardsdatascience.com/binary-hex-and-octal-in-python-20222488cee1",
+ "description": "Binary, octal, hex in python"
+ },
+ {
+ "url": "https://en.wikipedia.org/wiki/Numeral_system",
+ "description": "Numeral system"
+ }
+]
diff --git a/concepts/bitwise-operators/.meta/config.json b/concepts/bitwise-operators/.meta/config.json
index 9b9e8da5a9b..7767ff5d740 100644
--- a/concepts/bitwise-operators/.meta/config.json
+++ b/concepts/bitwise-operators/.meta/config.json
@@ -1,5 +1,5 @@
{
- "blurb": "TODO: add blurb for this concept",
- "authors": ["bethanyg", "cmccandless"],
+ "blurb": "Python supports bitwise operations such as left/right shift, and, or, xor, and not.",
+ "authors": ["BethanyG", "colinleach"],
"contributors": []
}
diff --git a/concepts/bitwise-operators/about.md b/concepts/bitwise-operators/about.md
index c628150d565..1cd5a237c29 100644
--- a/concepts/bitwise-operators/about.md
+++ b/concepts/bitwise-operators/about.md
@@ -1,2 +1,197 @@
-#TODO: Add about for this concept.
+# About
+Down at the hardware level, transistors can only be on or off: two states that we traditionally represent with `1` and `0`.
+These are the [`binary digits`][binary-digits], abbreviated as [`bits`][bits].
+Awareness of `bits` and `binary` is particularly important for systems programmers working in low-level languages.
+
+However, for most of the history of computing the programming priority has been to find increasingly sophisticated ways to _abstract away_ this binary reality.
+In Python (and many other [high-level programming languages][high-level-language]), we work with `int`, `float`, `string` and other defined _types_, up to and including audio and video formats.
+We let the Python internals take care of (eventually) translating everything to bits.
+
+Nevertheless, using [bitwise-operators][python-bitwise-operators] and [bitwise operations][python-bitwise-operations] can sometimes have significant advantages in speed and memory efficiency, even in a high-level language like Python.
+
+
+## Entering and Displaying Binary Numbers
+
+Unsurprisingly, Python interacts with the user using decimal numbers, but a programmer can override this default.
+In fact, Python will readily accept an `int` in `binary`, `hexadecimal`, or `octal` format, and will happily perform mathematical operations between them.
+For more details, you can review the [concept:python/binary-octal-hexadecimal]() concept.
+
+Binary numbers are entered with a `0b` prefix, just as `0x` can be used for hexadecimal (_hex numbers are a concise way to represent groups of 4 bits_), and `oct` can be used for octal numbers.
+
+There are multiple ways to convert integers to binary strings, varying in whether they include the `0b` prefix and whether they support left-padding with zeros:
+
+
+```python
+# Binary entry.
+>>> 0b10111
+23
+
+# Converting an int display to binary string, with prefix.
+>>> bin(23)
+'0b10111'
+
+>>> number = 23
+
+# Binary without prefix, padded to 8 digits.
+>>> format(number, '08b')
+'00010111'
+
+# Same format, but using an f-string.
+>>> f"{number} in decimal is {number:08b} in binary and {number:x} in hex"
+'23 in decimal is 00010111 in binary and 17 in hex'
+```
+
+
+## [`Bitwise Logic`][python-bitwise-operations]
+
+In the [concept:python/bools]() concept, we discussed the _logical operators_ `and`, `or` and `not` used with Boolean (_`True` and `False`_) values.
+The same logic rules apply when working with bits.
+
+However, the bitwise equivalents of the logical operators `&` (_and_), `|` (_or_), `~` (_not_), and `^` (_[XOR][xor]_), are applied to each _bit_ in a binary representation, treating `1` as `True` ("on") and `0` as `False` ("off").
+An example with the bitwise `&` might make this clearer:
+
+
+```python
+>>> x = 0b01100110
+>>> y = 0b00101010
+
+>>> format(x & y, '08b')
+'00100010'
+```
+
+Only positions with a `1` in _**both**_ the input numbers are set to `1` in the output.
+
+Bitwise `&` is commonly used as a way to isolate single bits in a compacted set of `True`/`False` values, such as user-configurable settings in an app.
+This enables the value of individual bits to control program logic:
+
+
+```python
+>>> number = 0b0110
+>>> number & 0b0001 > 0
+False
+
+>>> number & 0b0010 > 0
+True
+```
+
+
+For a bitwise `|` (or), a `1` is set in the output if there is a `1` in _**either**_ of the inputs:
+
+
+```python
+>>> x = 0b01100110
+>>> y = 0b00101010
+
+>>> format(x | y, '08b')
+'01101110'
+```
+
+
+With the `^` operator for bitwise e**x**clusive **or** (xor), a `1` is set if it appears in _**either**_ of the inputs _**but not both**_ inputs.
+This symbol might seem familiar from the [concept:python/sets]() concept, where it is used for `set` _symmetric difference_, which is the same as [xor applied to sets][symmetric-difference].
+If xor `^` seems strange, be aware that this is by far the [most common operation in cryptography][xor-cipher].
+
+
+```python
+>>> x = 0b01100110
+>>> y = 0b00101010
+
+>>> format(x ^ y, '08b')
+'01001100'
+```
+
+
+Finally, there is the `~` operator (_the [tilde][tilde] character_), which is a bitwise `not` that takes a single input and _**inverts all the bits**_, which might not be the result you were expecting!
+Each `1` in the representation changes to `0`, and vice versa.
+See the section below for details.
+
+
+## Negative Numbers and Binary Representation
+
+In decimal representation, we distinguish positive and negative numbers by using a `+` or `-` sign to the left of the digits.
+Using these symbols at a binary level proved inefficient for digital computing and raised the problem that `+0` is not the same as `-0`.
+
+Rather than using `-` and `+`, all modern computers use a [`two's complement`][twos-complement] representation for negative numbers, right down to the silicon chip level.
+This means that all bits are inverted and a number is _**interpreted as negative**_ if the left-most bit (also termed the "most significant bit", or MSB) is a `1`.
+Positive numbers have an MSB of `0`.
+This representation has the advantage of only having one version of zero, so that the programmer doesn't have to manage `-0` and `+0`.
+
+This way of representing negative and positive numbers adds a complication for Python: there are no finite-integer concepts like `int32` or `int64` internally in the core language.
+In 'modern' Python, `int`s are of unlimited size (_limited only by hardware capacity_), and a negative or bit-inverted number has a (_theoretically_) infinite number of `1`'s to the left, just as a positive number has unlimited `0`'s.
+
+This makes it difficult to give a useful example of `bitwise not`:
+
+```python
+>>> x = 0b01100110
+>>> format(x, '08b')
+'01100110'
+
+# This is a negative binary (not twos-complement display).
+>>> format(~x, '08b')
+'-1100111'
+
+ # Decimal representation.
+>>> x
+102
+
+# Using the Bitwise not, with an unintuitive result.
+>>> ~x
+-103
+```
+
+This is **not** the `0b10011001` we would see in languages with fixed-size integers.
+
+The `~` operator only works as expected with _**unsigned**_ byte or integer types, or with fixed-sized integer types.
+These numeric types are supported in third-party packages such as [`NumPy`][numpy], [`pandas`][pandas], and [`sympy`][sympy] but not in core Python.
+
+In practice, Python programmers quite often use `&`, `|`, `^`, and the shift operators described below with positive numbers only.
+Bitwise operations with negative numbers are much less common.
+One technique is to add [`2**32 (or 1 << 32)`][unsigned-int-python] to a negative value to make an `int` unsigned, but this gets difficult to manage.
+Another strategy is to work with the [`ctypes`][ctypes-module] module, and use c-style integer types, but this is equally unwieldy.
+
+
+## [`Shift operators`][bitwise-shift-operators]
+
+The left-shift operator `x << y` moves all the bits in `x` by `y` places to the left, filling the new gaps with zeros.
+Note that this is arithmetically identical to multiplying a number by `(2**y)`.
+
+The right-shift operator `x >> y` does the opposite.
+This is arithmetically identical to integer division `x // (2**y)`.
+
+Keep in mind the previous section on negative numbers and their pitfalls when shifting them in Python.
+
+
+```python
+>>> x = 8
+>>> format(x, '08b')
+'00001000'
+
+# A left bit shift.
+>>> x << 2
+32
+
+>>> format(x << 2, '08b')
+'00100000'
+
+# A right bit shift.
+>>> format(x >> 2, '08b')
+'00000010'
+```
+
+[binary-digits]: https://www.khanacademy.org/computing/computers-and-internet/xcae6f4a7ff015e7d:digital-information/xcae6f4a7ff015e7d:binary-numbers/v/the-binary-number-system
+[bits]: https://en.wikipedia.org/wiki/Bit
+[bitwise-shift-operators]: https://docs.python.org/3/reference/expressions.html#shifting-operations
+[ctypes-module]: https://docs.python.org/3/library/ctypes.html#module-ctypes
+[high-level-language]: https://en.wikipedia.org/wiki/High-level_programming_language
+[numpy]: https://numpy.org/doc/stable/user/basics.types.html
+[pandas]: https://pandas.pydata.org/docs/reference/arrays.html#nullable-integer
+[python-bitwise-operations]: https://docs.python.org/3/reference/expressions.html#binary-bitwise-operations
+[python-bitwise-operators]: https://docs.python.org/3/reference/expressions.html#binary-arithmetic-operations
+[symmetric-difference]: https://math.stackexchange.com/questions/84184/relation-between-xor-and-symmetric-difference#:~:text=It%20is%20the%20same%20thing,they%20are%20indeed%20the%20same.
+[sympy]: https://docs.sympy.org/latest/modules/codegen.html#predefined-types
+[tilde]: https://en.wikipedia.org/wiki/Tilde
+[twos-complement]: https://en.wikipedia.org/wiki/Two%27s_complement
+[unsigned-int-python]: https://stackoverflow.com/a/20768199
+[xor-cipher]: https://en.wikipedia.org/wiki/XOR_cipher
+[xor]: https://stackoverflow.com/a/2451393
diff --git a/concepts/bitwise-operators/introduction.md b/concepts/bitwise-operators/introduction.md
index bbe12ffd5e9..07833339ff2 100644
--- a/concepts/bitwise-operators/introduction.md
+++ b/concepts/bitwise-operators/introduction.md
@@ -1 +1,20 @@
-#TODO: Add introduction for this concept.
+# Introduction
+
+Down at the hardware level, [transistors can only be on or off][how-transistors-work]: two states that we traditionally represent with `1` and `0`.
+These are the [`binary digits`][binary-digits], abbreviated as [`bits`][bits].
+Awareness of `bits` and `binary` is particularly important for systems programmers working in low-level languages.
+However, for most of the history of computing the programming priority has been to find increasingly sophisticated ways to _abstract away_ this binary reality.
+
+
+In Python (and many other [high-level programming languages][high-level-language]), we work with `int`, `float`, `string` and other defined _types_, up to and including audio and video formats.
+Python internals take care of (_eventually_) translating all the higher-level data to bits.
+
+
+Nevertheless, using [bitwise-operators][python-bitwise-operators] and [bitwise operations][python-bitwise-operations] can sometimes have significant advantages in speed and memory efficiency, even in a high-level language like Python.
+
+[high-level-language]: https://en.wikipedia.org/wiki/High-level_programming_language
+[how-transistors-work]: https://www.build-electronic-circuits.com/how-transistors-work/
+[binary-digits]: https://www.khanacademy.org/computing/computers-and-internet/xcae6f4a7ff015e7d:digital-information/xcae6f4a7ff015e7d:binary-numbers/v/the-binary-number-system
+[bits]: https://en.wikipedia.org/wiki/Bit
+[python-bitwise-operations]: https://docs.python.org/3/reference/expressions.html#binary-bitwise-operations
+[python-bitwise-operators]: https://docs.python.org/3/reference/expressions.html#binary-arithmetic-operations
diff --git a/concepts/bitwise-operators/links.json b/concepts/bitwise-operators/links.json
index eb5fb7c38a5..ed251fab33a 100644
--- a/concepts/bitwise-operators/links.json
+++ b/concepts/bitwise-operators/links.json
@@ -1,18 +1,18 @@
[
{
- "url": "http://example.com/",
- "description": "TODO: add new link (above) and write a short description here of the resource."
+ "url": "https://realpython.com/python-bitwise-operators",
+ "description": "Real Python: Bitwise Operators in Python."
},
{
- "url": "http://example.com/",
- "description": "TODO: add new link (above) and write a short description here of the resource."
+ "url": "https://stackoverflow.com/a/20768199",
+ "description": "Stack Overflow: Convert a Python int to an unsigned int."
},
{
- "url": "http://example.com/",
- "description": "TODO: add new link (above) and write a short description here of the resource."
+ "url": "https://www.khanacademy.org/computing/computer-science/cryptography/ciphers/a/xor-bitwise-operation",
+ "description": "Khan Academy: The Ultimate Shift Cipher."
},
{
- "url": "http://example.com/",
- "description": "TODO: add new link (above) and write a short description here of the resource."
+ "url": "https://en.wikipedia.org/wiki/XOR_cipher",
+ "description": "The XOR Cipher"
}
]
diff --git a/concepts/bools/about.md b/concepts/bools/about.md
index 4b697270659..7015fdfafa4 100644
--- a/concepts/bools/about.md
+++ b/concepts/bools/about.md
@@ -1,6 +1,6 @@
# About
-Python represents True and False values with the [bool][bool] type.
+Python represents true and false values with the [`bool`][bools] type, which is a subclass of `int`.
There are only two Boolean values in this type: `True` and `False`.
These values can be assigned to a variable and combined with the [Boolean operators][boolean-operators] (`and`, `or`, `not`):
@@ -22,10 +22,10 @@ Each of the operators has a different precedence, where `not` is evaluated befor
Brackets can be used to evaluate one part of the expression before the others:
```python
->>>not True and True
+>>> not True and True
False
->>>not (True and False)
+>>> not (True and False)
True
```
@@ -45,25 +45,25 @@ A few `built-ins` are always considered `False` by definition:
```python
->>>bool(None)
+>>> bool(None)
False
->>>bool(1)
+>>> bool(1)
True
->>>bool(0)
+>>> bool(0)
False
->>>bool([1,2,3])
+>>> bool([1,2,3])
True
->>>bool([])
+>>> bool([])
False
->>>bool({"Pig" : 1, "Cow": 3})
+>>> bool({"Pig" : 1, "Cow": 3})
True
->>>bool({})
+>>> bool({})
False
```
@@ -95,10 +95,10 @@ The `bool` type is implemented as a _sub-type_ of _int_.
```python
->>>1 == True
+>>> 1 == True
True
->>>0 == False
+>>> 0 == False
True
```
@@ -106,14 +106,14 @@ However, `bools` are **still different** from `ints`, as noted when comparing th
```python
->>>1 is True
+>>> 1 is True
False
->>>0 is False
+>>> 0 is False
False
```
-> Note: in python >= 3.8, using a literal (such as 1, '', [], or {}) on the _left side_ of `is` will raise a warning.
+> Note: in python >= 3.8, using a literal (such as `1`, `''`, `[]`, or `{}`) on the _left side_ of `is` will raise a warning.
It is considered a [Python anti-pattern][comparing to true in the wrong way] to use the equality operator to compare a boolean variable to `True` or `False`.
@@ -134,8 +134,8 @@ It is considered a [Python anti-pattern][comparing to true in the wrong way] to
```
-[bool-function]: https://docs.python.org/3/library/functions.html#bool
-[bool]: https://docs.python.org/3/library/stdtypes.html#truth
[Boolean-operators]: https://docs.python.org/3/library/stdtypes.html#boolean-operations-and-or-not
+[bool-function]: https://docs.python.org/3/library/functions.html#bool
+[bools]: https://docs.python.org/3/library/stdtypes.html#typebool
[comparing to true in the wrong way]: https://docs.quantifiedcode.com/python-anti-patterns/readability/comparison_to_true.html
[comparisons]: https://docs.python.org/3/library/stdtypes.html#comparisons
diff --git a/concepts/bools/introduction.md b/concepts/bools/introduction.md
index 535f90be07c..85eb032df25 100644
--- a/concepts/bools/introduction.md
+++ b/concepts/bools/introduction.md
@@ -1,6 +1,6 @@
# Introduction
-Python represents true and false values with the `bool` type.
+Python represents true and false values with the [`bool`][bools] type, which is a subclass of `int`.
There are only two values under that type: `True` and `False`.
These values can be bound to a variable:
@@ -21,3 +21,5 @@ We can evaluate Boolean expressions using the `and`, `or`, and `not` operators.
>>> true_variable = not False
>>> false_variable = not True
```
+
+[bools]: https://docs.python.org/3/library/stdtypes.html#typebool
diff --git a/concepts/class-inheritance/about.md b/concepts/class-inheritance/about.md
index 5db7909e2c7..9f1bdf30cd9 100644
--- a/concepts/class-inheritance/about.md
+++ b/concepts/class-inheritance/about.md
@@ -7,7 +7,7 @@ In situations where only a small amount of functionality needs to be customized
`Inheritance` describes `is a kind of` relationship between two or more classes, abstracting common details into super (_base_ or _parent_) class and storing specific ones in the subclass (_derived class_ or _child class_).
-To create a child class, specify the parent class name inside the pair of parenthesis, followed by it's name.
+To create a child class, specify the parent class name inside the pair of parenthesis, followed by its name.
Example
```python
class Child(Parent):
diff --git a/concepts/classes/about.md b/concepts/classes/about.md
index f50af7321d3..9b6a8a0dfb7 100644
--- a/concepts/classes/about.md
+++ b/concepts/classes/about.md
@@ -118,7 +118,7 @@ class MyClass:
def __init__(self, location):
# This is an instance or object property, attribute, or variable.
- # Note that we are unpacking the tuple argument into two seperate instance variables.
+ # Note that we are unpacking the tuple argument into two separate instance variables.
self.location_x = location[0]
self.location_y = location[1]
@@ -185,10 +185,10 @@ class Demo:
The moment that `