termpet-pythonists 0.1.0__tar.gz → 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- termpet_pythonists-0.1.1/PKG-INFO +292 -0
- termpet_pythonists-0.1.1/README.md +280 -0
- {termpet_pythonists-0.1.0 → termpet_pythonists-0.1.1}/pyproject.toml +3 -2
- {termpet_pythonists-0.1.0 → termpet_pythonists-0.1.1}/src/termpet/__init__.py +2 -0
- termpet_pythonists-0.1.1/src/termpet_pythonists.egg-info/PKG-INFO +292 -0
- termpet_pythonists-0.1.0/PKG-INFO +0 -14
- termpet_pythonists-0.1.0/README.md +0 -2
- termpet_pythonists-0.1.0/src/termpet_pythonists.egg-info/PKG-INFO +0 -14
- {termpet_pythonists-0.1.0 → termpet_pythonists-0.1.1}/LICENSE +0 -0
- {termpet_pythonists-0.1.0 → termpet_pythonists-0.1.1}/setup.cfg +0 -0
- {termpet_pythonists-0.1.0 → termpet_pythonists-0.1.1}/src/termpet/constants.py +0 -0
- {termpet_pythonists-0.1.0 → termpet_pythonists-0.1.1}/src/termpet/feed.py +0 -0
- {termpet_pythonists-0.1.0 → termpet_pythonists-0.1.1}/src/termpet/pet.py +0 -0
- {termpet_pythonists-0.1.0 → termpet_pythonists-0.1.1}/src/termpet/play.py +0 -0
- {termpet_pythonists-0.1.0 → termpet_pythonists-0.1.1}/src/termpet/render.py +0 -0
- {termpet_pythonists-0.1.0 → termpet_pythonists-0.1.1}/src/termpet/sleep.py +0 -0
- {termpet_pythonists-0.1.0 → termpet_pythonists-0.1.1}/src/termpet/status.py +0 -0
- {termpet_pythonists-0.1.0 → termpet_pythonists-0.1.1}/src/termpet/storage.py +0 -0
- {termpet_pythonists-0.1.0 → termpet_pythonists-0.1.1}/src/termpet/tick.py +0 -0
- {termpet_pythonists-0.1.0 → termpet_pythonists-0.1.1}/src/termpet_pythonists.egg-info/SOURCES.txt +0 -0
- {termpet_pythonists-0.1.0 → termpet_pythonists-0.1.1}/src/termpet_pythonists.egg-info/dependency_links.txt +0 -0
- {termpet_pythonists-0.1.0 → termpet_pythonists-0.1.1}/src/termpet_pythonists.egg-info/top_level.txt +0 -0
- {termpet_pythonists-0.1.0 → termpet_pythonists-0.1.1}/tests/test_feed.py +0 -0
- {termpet_pythonists-0.1.0 → termpet_pythonists-0.1.1}/tests/test_pet.py +0 -0
- {termpet_pythonists-0.1.0 → termpet_pythonists-0.1.1}/tests/test_play.py +0 -0
- {termpet_pythonists-0.1.0 → termpet_pythonists-0.1.1}/tests/test_render.py +0 -0
- {termpet_pythonists-0.1.0 → termpet_pythonists-0.1.1}/tests/test_sleep.py +0 -0
- {termpet_pythonists-0.1.0 → termpet_pythonists-0.1.1}/tests/test_status.py +0 -0
- {termpet_pythonists-0.1.0 → termpet_pythonists-0.1.1}/tests/test_storage.py +0 -0
- {termpet_pythonists-0.1.0 → termpet_pythonists-0.1.1}/tests/test_tick.py +0 -0
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: termpet-pythonists
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: A playful terminal-pet Python package.
|
|
5
|
+
Author: Ray Yang, Ryan Lin, Jolie Leong, Layan Alyas, Hoang Ngo
|
|
6
|
+
License-Expression: GPL-3.0-or-later
|
|
7
|
+
Project-URL: Homepage, https://github.com/software-students-fall2026/python-package-exercise-pythonists
|
|
8
|
+
Requires-Python: >=3.12
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Dynamic: license-file
|
|
12
|
+
|
|
13
|
+
# termpet
|
|
14
|
+
|
|
15
|
+
[](https://github.com/software-students-fall2026/python-package-exercise-pythonists/actions/workflows/ci.yml)
|
|
16
|
+
|
|
17
|
+
A small virtual pet that lives in your terminal. Adopt a pet, feed it, play with it, let time pass, put it to sleep, and save it to a file so it's still there next time. Its mood changes with how well you look after it, and you can draw it as ASCII art.
|
|
18
|
+
|
|
19
|
+
**PyPI:** [termpet-pythonists](https://pypi.org/project/termpet-pythonists/)
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install termpet-pythonists
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The package installs as `termpet-pythonists` but you import it as `termpet`:
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
import termpet
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Requires Python 3.12 or newer.
|
|
34
|
+
|
|
35
|
+
## Quick start
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from termpet import Pet, feed, play, tick, sleep, mood, render, status, save, load
|
|
39
|
+
|
|
40
|
+
pet = Pet(name="Mochi", species="cat")
|
|
41
|
+
feed(pet, "steak")
|
|
42
|
+
tick(pet, 4)
|
|
43
|
+
sleep(pet, 2)
|
|
44
|
+
print(render(pet))
|
|
45
|
+
save(pet, "mochi.json")
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Each action function updates the pet you pass in and returns the same pet, so you can call them on their own or chain the result.
|
|
49
|
+
|
|
50
|
+
## Pet stats
|
|
51
|
+
|
|
52
|
+
Every pet has three stats, each from 0 to 100. All start at 50.
|
|
53
|
+
|
|
54
|
+
| Stat | 0 means | 100 means |
|
|
55
|
+
|---|---|---|
|
|
56
|
+
| `hunger` | starving | full |
|
|
57
|
+
| `energy` | exhausted | fully rested |
|
|
58
|
+
| `happiness` | miserable | ecstatic |
|
|
59
|
+
|
|
60
|
+
Stats are kept between 0 and 100 automatically; actions never push them outside that range.
|
|
61
|
+
|
|
62
|
+
## Functions
|
|
63
|
+
|
|
64
|
+
### `Pet(name, species, hunger=50, energy=50, happiness=50)`
|
|
65
|
+
|
|
66
|
+
Creates a pet. `name` and `species` are required and can be any non-blank text. The three stats are optional, which is useful for restoring a pet with known values.
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
pet = Pet(name="Mochi", species="cat")
|
|
70
|
+
tired = Pet(name="Biscuit", species="dog", energy=10)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Raises:
|
|
74
|
+
- `TypeError` if `name` or `species` isn't a string, or a stat isn't a number.
|
|
75
|
+
- `ValueError` if `name` or `species` is blank, or a stat is outside 0–100.
|
|
76
|
+
|
|
77
|
+
### `mood(pet)`
|
|
78
|
+
|
|
79
|
+
Returns the pet's mood as a string, based on the average of its three stats.
|
|
80
|
+
|
|
81
|
+
| Average stat | Mood |
|
|
82
|
+
|---|---|
|
|
83
|
+
| 75 or higher | `"ecstatic"` |
|
|
84
|
+
| 50 to 74 | `"content"` |
|
|
85
|
+
| 25 to 49 | `"grumpy"` |
|
|
86
|
+
| below 25 | `"feral"` |
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
>>> mood(Pet(name="Mochi", species="cat"))
|
|
90
|
+
'content'
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Raises `TypeError` if `pet` isn't a `Pet`.
|
|
94
|
+
|
|
95
|
+
### `feed(pet, food)`
|
|
96
|
+
|
|
97
|
+
Feeds the pet and returns it. Food names ignore case and surrounding spaces.
|
|
98
|
+
|
|
99
|
+
| Food | Hunger | Energy | Happiness |
|
|
100
|
+
|---|---|---|---|
|
|
101
|
+
| `apple` | +10 | +5 | +2 |
|
|
102
|
+
| `kibble` | +20 | 0 | +1 |
|
|
103
|
+
| `steak` | +35 | +5 | +10 |
|
|
104
|
+
| `treat` | +5 | +15 | +15 |
|
|
105
|
+
| `veggies` | +15 | 0 | 0 |
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
feed(pet, "treat")
|
|
109
|
+
feed(pet, " Steak ") # same as "steak"
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Raises:
|
|
113
|
+
- `TypeError` if `pet` isn't a `Pet` or `food` isn't a string.
|
|
114
|
+
- `ValueError` if `food` isn't one of the foods above.
|
|
115
|
+
|
|
116
|
+
### `play(pet, game, minutes=10)`
|
|
117
|
+
|
|
118
|
+
Plays a game with the pet for `minutes` minutes and returns it. Each game changes the pet's stats by a set amount per minute, so longer play has a bigger effect. Game names ignore case and surrounding spaces.
|
|
119
|
+
|
|
120
|
+
| Game | Hunger / min | Energy / min | Happiness / min |
|
|
121
|
+
|---|---|---|---|
|
|
122
|
+
| `fetch` | −1 | −1 | +2 |
|
|
123
|
+
| `hide & seek` | −0.5 | −0.5 | +1.5 |
|
|
124
|
+
| `tug of war` | −1.5 | −1.5 | +2.5 |
|
|
125
|
+
| `chase` | −2 | −2 | +3 |
|
|
126
|
+
|
|
127
|
+
`minutes` can be a whole or decimal number, greater than 0 and at most MAX_PLAY_MINUTES (60), defaulting at 10 minutes.
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
pet = Pet(name="Mochi", species="cat") # all stats start at 50
|
|
131
|
+
play(pet, "fetch") # 10 minutes default
|
|
132
|
+
print(pet.hunger, pet.energy, pet.happiness) # 40 40 70
|
|
133
|
+
|
|
134
|
+
play(pet, " Hide & Seek ", 5) # same as "hide & seek"
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Raises:
|
|
138
|
+
- `TypeError` if `pet` isn't a `Pet`, `game` isn't a string, or `minutes` isn't a number.
|
|
139
|
+
- `ValueError` if `game` isn't one of the games above, or `minutes` is 0 or less, or more than MAX_PLAY_MINUTES.
|
|
140
|
+
|
|
141
|
+
### `tick(pet, hours)`
|
|
142
|
+
|
|
143
|
+
Lets time pass and returns the pet. Each hour lowers its stats:
|
|
144
|
+
|
|
145
|
+
| Stat | Change per hour |
|
|
146
|
+
|---|---|
|
|
147
|
+
| `hunger` | −4 |
|
|
148
|
+
| `energy` | −2 |
|
|
149
|
+
| `happiness` | −3 |
|
|
150
|
+
|
|
151
|
+
`hours` can be a whole or decimal number, greater than 0 and at most 24.
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
tick(pet, 6) # six hours pass
|
|
155
|
+
tick(pet, 0.5) # half an hour passes
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Raises:
|
|
159
|
+
- `TypeError` if `pet` isn't a `Pet` or `hours` isn't a number.
|
|
160
|
+
- `ValueError` if `hours` is 0 or less, or more than 24.
|
|
161
|
+
|
|
162
|
+
### `sleep(pet, hours)`
|
|
163
|
+
|
|
164
|
+
Puts the pet to sleep and returns it. Each hour adds 10 energy and lowers hunger by 2. `hours` must be greater than 0 and at most 24.
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
sleep(pet, 8)
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Raises:
|
|
171
|
+
- `TypeError` if `pet` isn't a `Pet` or `hours` isn't a number.
|
|
172
|
+
- `ValueError` if `hours` is 0 or less, or more than 24.
|
|
173
|
+
|
|
174
|
+
### `render(pet, width=10)`
|
|
175
|
+
|
|
176
|
+
Returns the pet as ASCII art, with a face that matches its mood and a bar for each stat. `width` sets how many characters long each bar is.
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
>>> print(render(Pet(name="Mochi", species="cat", hunger=80, energy=70, happiness=90)))
|
|
180
|
+
/\_/\
|
|
181
|
+
( ^.^ ) Mochi the cat — ecstatic
|
|
182
|
+
> ^ <
|
|
183
|
+
Hunger [########--]
|
|
184
|
+
Energy [#######---]
|
|
185
|
+
Happiness [#########-]
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Faces by mood: `^.^` ecstatic, `o.o` content, `-.-` grumpy, `>.<` feral.
|
|
189
|
+
|
|
190
|
+
Raises:
|
|
191
|
+
- `TypeError` if `pet` isn't a `Pet` or `width` isn't a whole number.
|
|
192
|
+
- `ValueError` if `width` is less than 1.
|
|
193
|
+
|
|
194
|
+
### `status(pet, verbose=False)`
|
|
195
|
+
|
|
196
|
+
Returns a text summary of the pet's stats.
|
|
197
|
+
|
|
198
|
+
```python
|
|
199
|
+
>>> pet = Pet(name="Mochi", species="cat", hunger=30, energy=70, happiness=100)
|
|
200
|
+
>>> print(status(pet))
|
|
201
|
+
Mochi the cat | Hunger 30 | Energy 70 | Happiness 100
|
|
202
|
+
|
|
203
|
+
>>> print(status(pet, verbose=True))
|
|
204
|
+
Mochi the cat
|
|
205
|
+
Hunger ███░░░░░░░ 30
|
|
206
|
+
Energy ███████░░░ 70
|
|
207
|
+
Happiness ██████████ 100
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Raises `TypeError` if `pet` isn't a `Pet` or `verbose` isn't `True`/`False`.
|
|
211
|
+
|
|
212
|
+
### `save(pet, path)` and `load(path)`
|
|
213
|
+
|
|
214
|
+
Save a pet to a JSON file and load it back.
|
|
215
|
+
|
|
216
|
+
```python
|
|
217
|
+
save(pet, "mochi.json")
|
|
218
|
+
pet = load("mochi.json")
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
`save` overwrites an existing file. `path` can be a string or a `pathlib.Path`.
|
|
222
|
+
|
|
223
|
+
Raises:
|
|
224
|
+
- `TypeError` if `pet` isn't a `Pet` or `path` isn't a string or `Path`.
|
|
225
|
+
- `ValueError` from `save` if `path` is empty.
|
|
226
|
+
- `FileNotFoundError` from `load` if the file doesn't exist.
|
|
227
|
+
- `ValueError` from `load` if the file isn't valid JSON or doesn't describe a valid pet.
|
|
228
|
+
|
|
229
|
+
## Example program
|
|
230
|
+
|
|
231
|
+
[`examples/play_demo.py`](examples/play_demo.py) is an interactive demo and uses every function in the package.
|
|
232
|
+
|
|
233
|
+
With a fresh clone, after running `pipenv install --dev`:
|
|
234
|
+
```bash
|
|
235
|
+
pipenv run python examples/play_demo.py
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
If you installed the package from PyPI download the file and run:
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
python examples/play_demo.py
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Contributing
|
|
246
|
+
|
|
247
|
+
### Setup
|
|
248
|
+
|
|
249
|
+
You need Python 3.12+ and [pipenv](https://pipenv.pypa.io/).
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
git clone https://github.com/software-students-fall2026/python-package-exercise-pythonists.git
|
|
253
|
+
cd python-package-exercise-pythonists
|
|
254
|
+
pipenv install --dev
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
If `pipenv` isn't installed, run `python -m pip install --user pipenv` (or `brew install pipenv` on macOS). On macOS and Linux, use `python3` wherever these instructions say `python` if `python` isn't found.
|
|
258
|
+
|
|
259
|
+
### Run the tests
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
pipenv run python -m pytest
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Build the package
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
pipenv run python -m build
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
This creates a source distribution and a wheel in `dist/`.
|
|
272
|
+
|
|
273
|
+
### Workflow
|
|
274
|
+
|
|
275
|
+
1. Create a feature branch from `main`.
|
|
276
|
+
2. Make your changes and run the tests.
|
|
277
|
+
3. Push the branch and open a pull request into `main` on this repository, not the upstream course repository.
|
|
278
|
+
4. A teammate reviews the code and runs the tests before merging.
|
|
279
|
+
|
|
280
|
+
GitHub Actions runs the tests and builds the package on Python 3.12 and 3.13 for every pull request and push to `main`.
|
|
281
|
+
|
|
282
|
+
## Configuration
|
|
283
|
+
|
|
284
|
+
termpet needs no environment variables, secret files, or database.
|
|
285
|
+
|
|
286
|
+
## Team
|
|
287
|
+
|
|
288
|
+
- [Hoang Ngo](https://github.com/ogngnaoh)
|
|
289
|
+
- [Jolie Leong](https://github.com/jl15113)
|
|
290
|
+
- [Layan Alyas](https://github.com/layan-al)
|
|
291
|
+
- [Ray Yang](https://github.com/rayyang262)
|
|
292
|
+
- [Ryan Lin](https://github.com/ryanwlin)
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
# termpet
|
|
2
|
+
|
|
3
|
+
[](https://github.com/software-students-fall2026/python-package-exercise-pythonists/actions/workflows/ci.yml)
|
|
4
|
+
|
|
5
|
+
A small virtual pet that lives in your terminal. Adopt a pet, feed it, play with it, let time pass, put it to sleep, and save it to a file so it's still there next time. Its mood changes with how well you look after it, and you can draw it as ASCII art.
|
|
6
|
+
|
|
7
|
+
**PyPI:** [termpet-pythonists](https://pypi.org/project/termpet-pythonists/)
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install termpet-pythonists
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
The package installs as `termpet-pythonists` but you import it as `termpet`:
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
import termpet
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Requires Python 3.12 or newer.
|
|
22
|
+
|
|
23
|
+
## Quick start
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
from termpet import Pet, feed, play, tick, sleep, mood, render, status, save, load
|
|
27
|
+
|
|
28
|
+
pet = Pet(name="Mochi", species="cat")
|
|
29
|
+
feed(pet, "steak")
|
|
30
|
+
tick(pet, 4)
|
|
31
|
+
sleep(pet, 2)
|
|
32
|
+
print(render(pet))
|
|
33
|
+
save(pet, "mochi.json")
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Each action function updates the pet you pass in and returns the same pet, so you can call them on their own or chain the result.
|
|
37
|
+
|
|
38
|
+
## Pet stats
|
|
39
|
+
|
|
40
|
+
Every pet has three stats, each from 0 to 100. All start at 50.
|
|
41
|
+
|
|
42
|
+
| Stat | 0 means | 100 means |
|
|
43
|
+
|---|---|---|
|
|
44
|
+
| `hunger` | starving | full |
|
|
45
|
+
| `energy` | exhausted | fully rested |
|
|
46
|
+
| `happiness` | miserable | ecstatic |
|
|
47
|
+
|
|
48
|
+
Stats are kept between 0 and 100 automatically; actions never push them outside that range.
|
|
49
|
+
|
|
50
|
+
## Functions
|
|
51
|
+
|
|
52
|
+
### `Pet(name, species, hunger=50, energy=50, happiness=50)`
|
|
53
|
+
|
|
54
|
+
Creates a pet. `name` and `species` are required and can be any non-blank text. The three stats are optional, which is useful for restoring a pet with known values.
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
pet = Pet(name="Mochi", species="cat")
|
|
58
|
+
tired = Pet(name="Biscuit", species="dog", energy=10)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Raises:
|
|
62
|
+
- `TypeError` if `name` or `species` isn't a string, or a stat isn't a number.
|
|
63
|
+
- `ValueError` if `name` or `species` is blank, or a stat is outside 0–100.
|
|
64
|
+
|
|
65
|
+
### `mood(pet)`
|
|
66
|
+
|
|
67
|
+
Returns the pet's mood as a string, based on the average of its three stats.
|
|
68
|
+
|
|
69
|
+
| Average stat | Mood |
|
|
70
|
+
|---|---|
|
|
71
|
+
| 75 or higher | `"ecstatic"` |
|
|
72
|
+
| 50 to 74 | `"content"` |
|
|
73
|
+
| 25 to 49 | `"grumpy"` |
|
|
74
|
+
| below 25 | `"feral"` |
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
>>> mood(Pet(name="Mochi", species="cat"))
|
|
78
|
+
'content'
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Raises `TypeError` if `pet` isn't a `Pet`.
|
|
82
|
+
|
|
83
|
+
### `feed(pet, food)`
|
|
84
|
+
|
|
85
|
+
Feeds the pet and returns it. Food names ignore case and surrounding spaces.
|
|
86
|
+
|
|
87
|
+
| Food | Hunger | Energy | Happiness |
|
|
88
|
+
|---|---|---|---|
|
|
89
|
+
| `apple` | +10 | +5 | +2 |
|
|
90
|
+
| `kibble` | +20 | 0 | +1 |
|
|
91
|
+
| `steak` | +35 | +5 | +10 |
|
|
92
|
+
| `treat` | +5 | +15 | +15 |
|
|
93
|
+
| `veggies` | +15 | 0 | 0 |
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
feed(pet, "treat")
|
|
97
|
+
feed(pet, " Steak ") # same as "steak"
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Raises:
|
|
101
|
+
- `TypeError` if `pet` isn't a `Pet` or `food` isn't a string.
|
|
102
|
+
- `ValueError` if `food` isn't one of the foods above.
|
|
103
|
+
|
|
104
|
+
### `play(pet, game, minutes=10)`
|
|
105
|
+
|
|
106
|
+
Plays a game with the pet for `minutes` minutes and returns it. Each game changes the pet's stats by a set amount per minute, so longer play has a bigger effect. Game names ignore case and surrounding spaces.
|
|
107
|
+
|
|
108
|
+
| Game | Hunger / min | Energy / min | Happiness / min |
|
|
109
|
+
|---|---|---|---|
|
|
110
|
+
| `fetch` | −1 | −1 | +2 |
|
|
111
|
+
| `hide & seek` | −0.5 | −0.5 | +1.5 |
|
|
112
|
+
| `tug of war` | −1.5 | −1.5 | +2.5 |
|
|
113
|
+
| `chase` | −2 | −2 | +3 |
|
|
114
|
+
|
|
115
|
+
`minutes` can be a whole or decimal number, greater than 0 and at most MAX_PLAY_MINUTES (60), defaulting at 10 minutes.
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
pet = Pet(name="Mochi", species="cat") # all stats start at 50
|
|
119
|
+
play(pet, "fetch") # 10 minutes default
|
|
120
|
+
print(pet.hunger, pet.energy, pet.happiness) # 40 40 70
|
|
121
|
+
|
|
122
|
+
play(pet, " Hide & Seek ", 5) # same as "hide & seek"
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Raises:
|
|
126
|
+
- `TypeError` if `pet` isn't a `Pet`, `game` isn't a string, or `minutes` isn't a number.
|
|
127
|
+
- `ValueError` if `game` isn't one of the games above, or `minutes` is 0 or less, or more than MAX_PLAY_MINUTES.
|
|
128
|
+
|
|
129
|
+
### `tick(pet, hours)`
|
|
130
|
+
|
|
131
|
+
Lets time pass and returns the pet. Each hour lowers its stats:
|
|
132
|
+
|
|
133
|
+
| Stat | Change per hour |
|
|
134
|
+
|---|---|
|
|
135
|
+
| `hunger` | −4 |
|
|
136
|
+
| `energy` | −2 |
|
|
137
|
+
| `happiness` | −3 |
|
|
138
|
+
|
|
139
|
+
`hours` can be a whole or decimal number, greater than 0 and at most 24.
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
tick(pet, 6) # six hours pass
|
|
143
|
+
tick(pet, 0.5) # half an hour passes
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Raises:
|
|
147
|
+
- `TypeError` if `pet` isn't a `Pet` or `hours` isn't a number.
|
|
148
|
+
- `ValueError` if `hours` is 0 or less, or more than 24.
|
|
149
|
+
|
|
150
|
+
### `sleep(pet, hours)`
|
|
151
|
+
|
|
152
|
+
Puts the pet to sleep and returns it. Each hour adds 10 energy and lowers hunger by 2. `hours` must be greater than 0 and at most 24.
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
sleep(pet, 8)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Raises:
|
|
159
|
+
- `TypeError` if `pet` isn't a `Pet` or `hours` isn't a number.
|
|
160
|
+
- `ValueError` if `hours` is 0 or less, or more than 24.
|
|
161
|
+
|
|
162
|
+
### `render(pet, width=10)`
|
|
163
|
+
|
|
164
|
+
Returns the pet as ASCII art, with a face that matches its mood and a bar for each stat. `width` sets how many characters long each bar is.
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
>>> print(render(Pet(name="Mochi", species="cat", hunger=80, energy=70, happiness=90)))
|
|
168
|
+
/\_/\
|
|
169
|
+
( ^.^ ) Mochi the cat — ecstatic
|
|
170
|
+
> ^ <
|
|
171
|
+
Hunger [########--]
|
|
172
|
+
Energy [#######---]
|
|
173
|
+
Happiness [#########-]
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Faces by mood: `^.^` ecstatic, `o.o` content, `-.-` grumpy, `>.<` feral.
|
|
177
|
+
|
|
178
|
+
Raises:
|
|
179
|
+
- `TypeError` if `pet` isn't a `Pet` or `width` isn't a whole number.
|
|
180
|
+
- `ValueError` if `width` is less than 1.
|
|
181
|
+
|
|
182
|
+
### `status(pet, verbose=False)`
|
|
183
|
+
|
|
184
|
+
Returns a text summary of the pet's stats.
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
>>> pet = Pet(name="Mochi", species="cat", hunger=30, energy=70, happiness=100)
|
|
188
|
+
>>> print(status(pet))
|
|
189
|
+
Mochi the cat | Hunger 30 | Energy 70 | Happiness 100
|
|
190
|
+
|
|
191
|
+
>>> print(status(pet, verbose=True))
|
|
192
|
+
Mochi the cat
|
|
193
|
+
Hunger ███░░░░░░░ 30
|
|
194
|
+
Energy ███████░░░ 70
|
|
195
|
+
Happiness ██████████ 100
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Raises `TypeError` if `pet` isn't a `Pet` or `verbose` isn't `True`/`False`.
|
|
199
|
+
|
|
200
|
+
### `save(pet, path)` and `load(path)`
|
|
201
|
+
|
|
202
|
+
Save a pet to a JSON file and load it back.
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
save(pet, "mochi.json")
|
|
206
|
+
pet = load("mochi.json")
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
`save` overwrites an existing file. `path` can be a string or a `pathlib.Path`.
|
|
210
|
+
|
|
211
|
+
Raises:
|
|
212
|
+
- `TypeError` if `pet` isn't a `Pet` or `path` isn't a string or `Path`.
|
|
213
|
+
- `ValueError` from `save` if `path` is empty.
|
|
214
|
+
- `FileNotFoundError` from `load` if the file doesn't exist.
|
|
215
|
+
- `ValueError` from `load` if the file isn't valid JSON or doesn't describe a valid pet.
|
|
216
|
+
|
|
217
|
+
## Example program
|
|
218
|
+
|
|
219
|
+
[`examples/play_demo.py`](examples/play_demo.py) is an interactive demo and uses every function in the package.
|
|
220
|
+
|
|
221
|
+
With a fresh clone, after running `pipenv install --dev`:
|
|
222
|
+
```bash
|
|
223
|
+
pipenv run python examples/play_demo.py
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
If you installed the package from PyPI download the file and run:
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
python examples/play_demo.py
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## Contributing
|
|
234
|
+
|
|
235
|
+
### Setup
|
|
236
|
+
|
|
237
|
+
You need Python 3.12+ and [pipenv](https://pipenv.pypa.io/).
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
git clone https://github.com/software-students-fall2026/python-package-exercise-pythonists.git
|
|
241
|
+
cd python-package-exercise-pythonists
|
|
242
|
+
pipenv install --dev
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
If `pipenv` isn't installed, run `python -m pip install --user pipenv` (or `brew install pipenv` on macOS). On macOS and Linux, use `python3` wherever these instructions say `python` if `python` isn't found.
|
|
246
|
+
|
|
247
|
+
### Run the tests
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
pipenv run python -m pytest
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Build the package
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
pipenv run python -m build
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
This creates a source distribution and a wheel in `dist/`.
|
|
260
|
+
|
|
261
|
+
### Workflow
|
|
262
|
+
|
|
263
|
+
1. Create a feature branch from `main`.
|
|
264
|
+
2. Make your changes and run the tests.
|
|
265
|
+
3. Push the branch and open a pull request into `main` on this repository, not the upstream course repository.
|
|
266
|
+
4. A teammate reviews the code and runs the tests before merging.
|
|
267
|
+
|
|
268
|
+
GitHub Actions runs the tests and builds the package on Python 3.12 and 3.13 for every pull request and push to `main`.
|
|
269
|
+
|
|
270
|
+
## Configuration
|
|
271
|
+
|
|
272
|
+
termpet needs no environment variables, secret files, or database.
|
|
273
|
+
|
|
274
|
+
## Team
|
|
275
|
+
|
|
276
|
+
- [Hoang Ngo](https://github.com/ogngnaoh)
|
|
277
|
+
- [Jolie Leong](https://github.com/jl15113)
|
|
278
|
+
- [Layan Alyas](https://github.com/layan-al)
|
|
279
|
+
- [Ray Yang](https://github.com/rayyang262)
|
|
280
|
+
- [Ryan Lin](https://github.com/ryanwlin)
|
|
@@ -4,15 +4,16 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "termpet-pythonists"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.1"
|
|
8
8
|
description = "A playful terminal-pet Python package."
|
|
9
9
|
readme = "README.md"
|
|
10
|
-
requires-python = ">=3.
|
|
10
|
+
requires-python = ">=3.12"
|
|
11
11
|
authors = [
|
|
12
12
|
{ name = "Ray Yang" },
|
|
13
13
|
{ name = "Ryan Lin" },
|
|
14
14
|
{ name = "Jolie Leong" },
|
|
15
15
|
{ name = "Layan Alyas" },
|
|
16
|
+
{name = "Hoang Ngo" }
|
|
16
17
|
]
|
|
17
18
|
license = "GPL-3.0-or-later"
|
|
18
19
|
license-files = ["LICENSE"]
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: termpet-pythonists
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: A playful terminal-pet Python package.
|
|
5
|
+
Author: Ray Yang, Ryan Lin, Jolie Leong, Layan Alyas, Hoang Ngo
|
|
6
|
+
License-Expression: GPL-3.0-or-later
|
|
7
|
+
Project-URL: Homepage, https://github.com/software-students-fall2026/python-package-exercise-pythonists
|
|
8
|
+
Requires-Python: >=3.12
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Dynamic: license-file
|
|
12
|
+
|
|
13
|
+
# termpet
|
|
14
|
+
|
|
15
|
+
[](https://github.com/software-students-fall2026/python-package-exercise-pythonists/actions/workflows/ci.yml)
|
|
16
|
+
|
|
17
|
+
A small virtual pet that lives in your terminal. Adopt a pet, feed it, play with it, let time pass, put it to sleep, and save it to a file so it's still there next time. Its mood changes with how well you look after it, and you can draw it as ASCII art.
|
|
18
|
+
|
|
19
|
+
**PyPI:** [termpet-pythonists](https://pypi.org/project/termpet-pythonists/)
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install termpet-pythonists
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The package installs as `termpet-pythonists` but you import it as `termpet`:
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
import termpet
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Requires Python 3.12 or newer.
|
|
34
|
+
|
|
35
|
+
## Quick start
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from termpet import Pet, feed, play, tick, sleep, mood, render, status, save, load
|
|
39
|
+
|
|
40
|
+
pet = Pet(name="Mochi", species="cat")
|
|
41
|
+
feed(pet, "steak")
|
|
42
|
+
tick(pet, 4)
|
|
43
|
+
sleep(pet, 2)
|
|
44
|
+
print(render(pet))
|
|
45
|
+
save(pet, "mochi.json")
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Each action function updates the pet you pass in and returns the same pet, so you can call them on their own or chain the result.
|
|
49
|
+
|
|
50
|
+
## Pet stats
|
|
51
|
+
|
|
52
|
+
Every pet has three stats, each from 0 to 100. All start at 50.
|
|
53
|
+
|
|
54
|
+
| Stat | 0 means | 100 means |
|
|
55
|
+
|---|---|---|
|
|
56
|
+
| `hunger` | starving | full |
|
|
57
|
+
| `energy` | exhausted | fully rested |
|
|
58
|
+
| `happiness` | miserable | ecstatic |
|
|
59
|
+
|
|
60
|
+
Stats are kept between 0 and 100 automatically; actions never push them outside that range.
|
|
61
|
+
|
|
62
|
+
## Functions
|
|
63
|
+
|
|
64
|
+
### `Pet(name, species, hunger=50, energy=50, happiness=50)`
|
|
65
|
+
|
|
66
|
+
Creates a pet. `name` and `species` are required and can be any non-blank text. The three stats are optional, which is useful for restoring a pet with known values.
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
pet = Pet(name="Mochi", species="cat")
|
|
70
|
+
tired = Pet(name="Biscuit", species="dog", energy=10)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Raises:
|
|
74
|
+
- `TypeError` if `name` or `species` isn't a string, or a stat isn't a number.
|
|
75
|
+
- `ValueError` if `name` or `species` is blank, or a stat is outside 0–100.
|
|
76
|
+
|
|
77
|
+
### `mood(pet)`
|
|
78
|
+
|
|
79
|
+
Returns the pet's mood as a string, based on the average of its three stats.
|
|
80
|
+
|
|
81
|
+
| Average stat | Mood |
|
|
82
|
+
|---|---|
|
|
83
|
+
| 75 or higher | `"ecstatic"` |
|
|
84
|
+
| 50 to 74 | `"content"` |
|
|
85
|
+
| 25 to 49 | `"grumpy"` |
|
|
86
|
+
| below 25 | `"feral"` |
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
>>> mood(Pet(name="Mochi", species="cat"))
|
|
90
|
+
'content'
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Raises `TypeError` if `pet` isn't a `Pet`.
|
|
94
|
+
|
|
95
|
+
### `feed(pet, food)`
|
|
96
|
+
|
|
97
|
+
Feeds the pet and returns it. Food names ignore case and surrounding spaces.
|
|
98
|
+
|
|
99
|
+
| Food | Hunger | Energy | Happiness |
|
|
100
|
+
|---|---|---|---|
|
|
101
|
+
| `apple` | +10 | +5 | +2 |
|
|
102
|
+
| `kibble` | +20 | 0 | +1 |
|
|
103
|
+
| `steak` | +35 | +5 | +10 |
|
|
104
|
+
| `treat` | +5 | +15 | +15 |
|
|
105
|
+
| `veggies` | +15 | 0 | 0 |
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
feed(pet, "treat")
|
|
109
|
+
feed(pet, " Steak ") # same as "steak"
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Raises:
|
|
113
|
+
- `TypeError` if `pet` isn't a `Pet` or `food` isn't a string.
|
|
114
|
+
- `ValueError` if `food` isn't one of the foods above.
|
|
115
|
+
|
|
116
|
+
### `play(pet, game, minutes=10)`
|
|
117
|
+
|
|
118
|
+
Plays a game with the pet for `minutes` minutes and returns it. Each game changes the pet's stats by a set amount per minute, so longer play has a bigger effect. Game names ignore case and surrounding spaces.
|
|
119
|
+
|
|
120
|
+
| Game | Hunger / min | Energy / min | Happiness / min |
|
|
121
|
+
|---|---|---|---|
|
|
122
|
+
| `fetch` | −1 | −1 | +2 |
|
|
123
|
+
| `hide & seek` | −0.5 | −0.5 | +1.5 |
|
|
124
|
+
| `tug of war` | −1.5 | −1.5 | +2.5 |
|
|
125
|
+
| `chase` | −2 | −2 | +3 |
|
|
126
|
+
|
|
127
|
+
`minutes` can be a whole or decimal number, greater than 0 and at most MAX_PLAY_MINUTES (60), defaulting at 10 minutes.
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
pet = Pet(name="Mochi", species="cat") # all stats start at 50
|
|
131
|
+
play(pet, "fetch") # 10 minutes default
|
|
132
|
+
print(pet.hunger, pet.energy, pet.happiness) # 40 40 70
|
|
133
|
+
|
|
134
|
+
play(pet, " Hide & Seek ", 5) # same as "hide & seek"
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Raises:
|
|
138
|
+
- `TypeError` if `pet` isn't a `Pet`, `game` isn't a string, or `minutes` isn't a number.
|
|
139
|
+
- `ValueError` if `game` isn't one of the games above, or `minutes` is 0 or less, or more than MAX_PLAY_MINUTES.
|
|
140
|
+
|
|
141
|
+
### `tick(pet, hours)`
|
|
142
|
+
|
|
143
|
+
Lets time pass and returns the pet. Each hour lowers its stats:
|
|
144
|
+
|
|
145
|
+
| Stat | Change per hour |
|
|
146
|
+
|---|---|
|
|
147
|
+
| `hunger` | −4 |
|
|
148
|
+
| `energy` | −2 |
|
|
149
|
+
| `happiness` | −3 |
|
|
150
|
+
|
|
151
|
+
`hours` can be a whole or decimal number, greater than 0 and at most 24.
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
tick(pet, 6) # six hours pass
|
|
155
|
+
tick(pet, 0.5) # half an hour passes
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Raises:
|
|
159
|
+
- `TypeError` if `pet` isn't a `Pet` or `hours` isn't a number.
|
|
160
|
+
- `ValueError` if `hours` is 0 or less, or more than 24.
|
|
161
|
+
|
|
162
|
+
### `sleep(pet, hours)`
|
|
163
|
+
|
|
164
|
+
Puts the pet to sleep and returns it. Each hour adds 10 energy and lowers hunger by 2. `hours` must be greater than 0 and at most 24.
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
sleep(pet, 8)
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Raises:
|
|
171
|
+
- `TypeError` if `pet` isn't a `Pet` or `hours` isn't a number.
|
|
172
|
+
- `ValueError` if `hours` is 0 or less, or more than 24.
|
|
173
|
+
|
|
174
|
+
### `render(pet, width=10)`
|
|
175
|
+
|
|
176
|
+
Returns the pet as ASCII art, with a face that matches its mood and a bar for each stat. `width` sets how many characters long each bar is.
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
>>> print(render(Pet(name="Mochi", species="cat", hunger=80, energy=70, happiness=90)))
|
|
180
|
+
/\_/\
|
|
181
|
+
( ^.^ ) Mochi the cat — ecstatic
|
|
182
|
+
> ^ <
|
|
183
|
+
Hunger [########--]
|
|
184
|
+
Energy [#######---]
|
|
185
|
+
Happiness [#########-]
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Faces by mood: `^.^` ecstatic, `o.o` content, `-.-` grumpy, `>.<` feral.
|
|
189
|
+
|
|
190
|
+
Raises:
|
|
191
|
+
- `TypeError` if `pet` isn't a `Pet` or `width` isn't a whole number.
|
|
192
|
+
- `ValueError` if `width` is less than 1.
|
|
193
|
+
|
|
194
|
+
### `status(pet, verbose=False)`
|
|
195
|
+
|
|
196
|
+
Returns a text summary of the pet's stats.
|
|
197
|
+
|
|
198
|
+
```python
|
|
199
|
+
>>> pet = Pet(name="Mochi", species="cat", hunger=30, energy=70, happiness=100)
|
|
200
|
+
>>> print(status(pet))
|
|
201
|
+
Mochi the cat | Hunger 30 | Energy 70 | Happiness 100
|
|
202
|
+
|
|
203
|
+
>>> print(status(pet, verbose=True))
|
|
204
|
+
Mochi the cat
|
|
205
|
+
Hunger ███░░░░░░░ 30
|
|
206
|
+
Energy ███████░░░ 70
|
|
207
|
+
Happiness ██████████ 100
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Raises `TypeError` if `pet` isn't a `Pet` or `verbose` isn't `True`/`False`.
|
|
211
|
+
|
|
212
|
+
### `save(pet, path)` and `load(path)`
|
|
213
|
+
|
|
214
|
+
Save a pet to a JSON file and load it back.
|
|
215
|
+
|
|
216
|
+
```python
|
|
217
|
+
save(pet, "mochi.json")
|
|
218
|
+
pet = load("mochi.json")
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
`save` overwrites an existing file. `path` can be a string or a `pathlib.Path`.
|
|
222
|
+
|
|
223
|
+
Raises:
|
|
224
|
+
- `TypeError` if `pet` isn't a `Pet` or `path` isn't a string or `Path`.
|
|
225
|
+
- `ValueError` from `save` if `path` is empty.
|
|
226
|
+
- `FileNotFoundError` from `load` if the file doesn't exist.
|
|
227
|
+
- `ValueError` from `load` if the file isn't valid JSON or doesn't describe a valid pet.
|
|
228
|
+
|
|
229
|
+
## Example program
|
|
230
|
+
|
|
231
|
+
[`examples/play_demo.py`](examples/play_demo.py) is an interactive demo and uses every function in the package.
|
|
232
|
+
|
|
233
|
+
With a fresh clone, after running `pipenv install --dev`:
|
|
234
|
+
```bash
|
|
235
|
+
pipenv run python examples/play_demo.py
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
If you installed the package from PyPI download the file and run:
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
python examples/play_demo.py
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Contributing
|
|
246
|
+
|
|
247
|
+
### Setup
|
|
248
|
+
|
|
249
|
+
You need Python 3.12+ and [pipenv](https://pipenv.pypa.io/).
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
git clone https://github.com/software-students-fall2026/python-package-exercise-pythonists.git
|
|
253
|
+
cd python-package-exercise-pythonists
|
|
254
|
+
pipenv install --dev
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
If `pipenv` isn't installed, run `python -m pip install --user pipenv` (or `brew install pipenv` on macOS). On macOS and Linux, use `python3` wherever these instructions say `python` if `python` isn't found.
|
|
258
|
+
|
|
259
|
+
### Run the tests
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
pipenv run python -m pytest
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Build the package
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
pipenv run python -m build
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
This creates a source distribution and a wheel in `dist/`.
|
|
272
|
+
|
|
273
|
+
### Workflow
|
|
274
|
+
|
|
275
|
+
1. Create a feature branch from `main`.
|
|
276
|
+
2. Make your changes and run the tests.
|
|
277
|
+
3. Push the branch and open a pull request into `main` on this repository, not the upstream course repository.
|
|
278
|
+
4. A teammate reviews the code and runs the tests before merging.
|
|
279
|
+
|
|
280
|
+
GitHub Actions runs the tests and builds the package on Python 3.12 and 3.13 for every pull request and push to `main`.
|
|
281
|
+
|
|
282
|
+
## Configuration
|
|
283
|
+
|
|
284
|
+
termpet needs no environment variables, secret files, or database.
|
|
285
|
+
|
|
286
|
+
## Team
|
|
287
|
+
|
|
288
|
+
- [Hoang Ngo](https://github.com/ogngnaoh)
|
|
289
|
+
- [Jolie Leong](https://github.com/jl15113)
|
|
290
|
+
- [Layan Alyas](https://github.com/layan-al)
|
|
291
|
+
- [Ray Yang](https://github.com/rayyang262)
|
|
292
|
+
- [Ryan Lin](https://github.com/ryanwlin)
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: termpet-pythonists
|
|
3
|
-
Version: 0.1.0
|
|
4
|
-
Summary: A playful terminal-pet Python package.
|
|
5
|
-
Author: Ray Yang, Ryan Lin, Jolie Leong, Layan Alyas
|
|
6
|
-
License-Expression: GPL-3.0-or-later
|
|
7
|
-
Project-URL: Homepage, https://github.com/software-students-fall2026/python-package-exercise-pythonists
|
|
8
|
-
Requires-Python: >=3.10
|
|
9
|
-
Description-Content-Type: text/markdown
|
|
10
|
-
License-File: LICENSE
|
|
11
|
-
Dynamic: license-file
|
|
12
|
-
|
|
13
|
-
# Python Package Exercise
|
|
14
|
-

|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: termpet-pythonists
|
|
3
|
-
Version: 0.1.0
|
|
4
|
-
Summary: A playful terminal-pet Python package.
|
|
5
|
-
Author: Ray Yang, Ryan Lin, Jolie Leong, Layan Alyas
|
|
6
|
-
License-Expression: GPL-3.0-or-later
|
|
7
|
-
Project-URL: Homepage, https://github.com/software-students-fall2026/python-package-exercise-pythonists
|
|
8
|
-
Requires-Python: >=3.10
|
|
9
|
-
Description-Content-Type: text/markdown
|
|
10
|
-
License-File: LICENSE
|
|
11
|
-
Dynamic: license-file
|
|
12
|
-
|
|
13
|
-
# Python Package Exercise
|
|
14
|
-

|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{termpet_pythonists-0.1.0 → termpet_pythonists-0.1.1}/src/termpet_pythonists.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{termpet_pythonists-0.1.0 → termpet_pythonists-0.1.1}/src/termpet_pythonists.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|