1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
|
# 🐶🕵️ Scooby
[](https://pypi.org/project/scooby/)
[](https://github.com/banesullivan/scooby/actions/workflows/pythonpackage.yml)
[](https://pypi.org/project/scooby/)
[](https://anaconda.org/conda-forge/scooby)
[](https://codecov.io/gh/banesullivan/scooby)
*Great Dane turned Python environment detective*
This is a lightweight tool for easily reporting your Python environment's
package versions and hardware resources.
Install from [PyPI](https://pypi.org/project/scooby/)
```bash
pip install scooby
```
or from [conda-forge](https://anaconda.org/conda-forge/scooby/)
```bash
conda install -c conda-forge scooby
```

Scooby has HTML formatting for Jupyter notebooks and rich text formatting for
just about every other environment. We designed this module to be lightweight
such that it could easily be added as a dependency to Python projects for
environment reporting when debugging. Simply add scooby to your dependencies
and implement a function to have scooby report on the aspects of the
environment you care most about.
If scooby is unable to detect aspects of an environment that you'd like to
know, please share this with us as a feature requests or pull requests.
The scooby reporting is derived from the versioning-scripts created by [Dieter
Werthmüller](https://github.com/prisae) for
[empymod](https://empymod.github.io), [emg3d](https://empymod.github.io), and
the [SimPEG](https://github.com/simpeg/) framework. It was heavily inspired by
`ipynbtools.py` from [qutip](https://github.com/qutip) and
[`watermark.py`](https://github.com/rasbt/watermark). This package has been
altered to create a lightweight implementation so that it can easily be used as
an environment reporting tool in any Python library with minimal impact.
## Usage
### Generating Reports
Reports are rendered as html-tables in Jupyter notebooks as shown in the
screenshot above, and otherwise as plain text lists. If you do not output the
`Report` object either at the end of a notebook cell or it is generated
somewhere in a vanilla Python script, you may have to print the `Report`
object: `print(scooby.Report())`, but note that this will only output the plain
text representation of the script.
```py
>>> import scooby
>>> scooby.Report()
```
```
--------------------------------------------------------------------------------
Date: Wed Feb 12 15:35:43 2020 W. Europe Standard Time
OS : Windows
CPU(s) : 16
Machine : AMD64
Architecture : 64bit
RAM : 31.9 GiB
Environment : IPython
Python 3.7.6 | packaged by conda-forge | (default, Jan 7 2020, 21:48:41)
[MSC v.1916 64 bit (AMD64)]
numpy : 1.18.1
scipy : 1.3.1
IPython : 7.12.0
matplotlib : 3.0.3
scooby : 0.5.0
Intel(R) Math Kernel Library Version 2019.0.4 Product Build 20190411 for
Intel(R) 64 architecture applications
--------------------------------------------------------------------------------
```
For all the Scooby-Doo fans out there, `doo` is an alias for `Report` so you
can oh-so satisfyingly do:
```py
>>> import scooby
>>> scooby.doo()
```
```
--------------------------------------------------------------------------------
Date: Thu Nov 25 09:47:50 2021 MST
OS : Darwin
CPU(s) : 12
Machine : x86_64
Architecture : 64bit
RAM : 32.0 GiB
Environment : Python
File system : apfs
Python 3.8.12 | packaged by conda-forge | (default, Oct 12 2021, 21:50:38)
[Clang 11.1.0 ]
numpy : 1.21.4
scipy : 1.7.3
IPython : 7.29.0
matplotlib : 3.5.0
scooby : 0.5.8
--------------------------------------------------------------------------------
```
Or better yet:
```py
from scooby import doo as doobiedoo
```
On top of the default (optional) packages you can provide additional packages,
either as strings or give already imported packages:
```py
>>> import pyvista
>>> import scooby
>>> scooby.Report(additional=[pyvista, 'vtk', 'no_version', 'does_not_exist'])
```
```
--------------------------------------------------------------------------------
Date: Wed Feb 12 16:15:15 2020 W. Europe Standard Time
OS : Windows
CPU(s) : 16
Machine : AMD64
Architecture : 64bit
RAM : 31.9 GiB
Environment : IPython
Python 3.7.6 | packaged by conda-forge | (default, Jan 7 2020, 21:48:41)
[MSC v.1916 64 bit (AMD64)]
pyvista : 0.23.1
vtk : 8.1.2
no_version : Version unknown
does_not_exist : Could not import
numpy : 1.18.1
scipy : 1.3.1
IPython : 7.12.0
matplotlib : 3.0.3
scooby : 0.5.0
Intel(R) Math Kernel Library Version 2019.0.4 Product Build 20190411 for
Intel(R) 64 architecture applications
--------------------------------------------------------------------------------
```
Furthermore, scooby reports if a package could not be imported or if the
version of a package could not be determined.
Other useful parameters are
- `ncol`: number of columns in the html-table;
- `text_width`: text width of the plain-text version;
- `sort`: list is sorted alphabetically if True.
Besides `additional` there are two more lists, `core` and `optional`, which
can be used to provide package names. However, they are mostly useful for
package maintainers wanting to use scooby to create their reporting system
(see below).
### Implementing scooby in your project
You can easily generate a custom `Report` instance using scooby within your
project:
```py
class Report(scooby.Report):
def __init__(self, additional=None, ncol=3, text_width=80, sort=False):
"""Initiate a scooby.Report instance."""
# Mandatory packages.
core = ['yourpackage', 'your_core_packages', 'e.g.', 'numpy', 'scooby']
# Optional packages.
optional = ['your_optional_packages', 'e.g.', 'matplotlib']
scooby.Report.__init__(self, additional=additional, core=core,
optional=optional, ncol=ncol,
text_width=text_width, sort=sort)
```
This makes it particularly easy for a user of your project to quickly generate
a report on all of the relevant package versions and environment details when
submitting a bug.
```py
>>> import your_package
>>> your_package.Report()
```
The packages on the `core`-list are the mandatory ones for your project, while
the `optional`-list can be used for optional packages. Keep the
`additional`-list free to allow your users to add packages to the list.
#### Implementing as a soft dependency
If you would like to implement scooby, but are hesitant to add another
dependency to your package, here is an easy way how you can use scooby as a
soft dependency. Instead of `import scooby` use the following snippet:
```py
# Make scooby a soft dependency:
try:
from scooby import Report as ScoobyReport
except ImportError:
class ScoobyReport:
def __init__(self, *args, **kwargs):
message = (
'\n *ERROR*: `Report` requires `scooby`.'
'\n Install it via `pip install scooby` or'
'\n `conda install -c conda-forge scooby`.\n'
)
raise ImportError(message)
```
and then create your own `Report` class same as above,
```py
class Report(ScoobyReport):
def __init__(self, additional=None, ncol=3, text_width=80, sort=False):
"""Initiate a scooby.Report instance."""
# Mandatory packages.
core = ['yourpackage', 'your_core_packages', 'e.g.', 'numpy', 'scooby']
# Optional packages.
optional = ['your_optional_packages', 'e.g.', 'matplotlib']
scooby.Report.__init__(self, additional=additional, core=core,
optional=optional, ncol=ncol,
text_width=text_width, sort=sort)
```
If a user has scooby installed, all works as expected. If scooby is not
installed, it will raise the following exception:
```py
>>> import your_package
>>> your_package.Report()
*ERROR*: `Report` requires `scooby`
Install it via `pip install scooby` or
`conda install -c conda-forge scooby`.
```
### Autogenerate Reports for any Packages
Scooby can automatically generate a Report for any package and its
distribution requirements with the `AutoReport` class:
```py
>>> import scooby
>>> scooby.AutoReport('matplotlib')
```
```
--------------------------------------------------------------------------------
Date: Fri Oct 20 16:49:34 2023 PDT
OS : Darwin
CPU(s) : 8
Machine : arm64
Architecture : 64bit
RAM : 16.0 GiB
Environment : Python
File system : apfs
Python 3.11.3 | packaged by conda-forge | (main, Apr 6 2023, 08:58:31)
[Clang 14.0.6 ]
matplotlib : 3.7.1
contourpy : 1.0.7
cycler : 0.11.0
fonttools : 4.39.4
kiwisolver : 1.4.4
numpy : 1.24.3
packaging : 23.1
pillow : 9.5.0
pyparsing : 3.0.9
python-dateutil : 2.8.2
--------------------------------------------------------------------------------
```
### Solving Mysteries
Are you struggling with the mystery of whether or not code is being executed in
IPython, Jupyter, or normal Python? Try using some of scooby's investigative
functions to solve these kinds of mysteries:
```py
import scooby
if scooby.in_ipykernel():
# Do Jupyter/IPyKernel stuff
elif scooby.in_ipython():
# Do IPython stuff
else:
# Do normal, boring Python stuff
```
### How does scooby get version numbers?
A couple of locations are checked, and we are happy to implement more if
needed, just open an issue!
Currently, it looks in the following places:
- `__version__`
- `version`
- lookup `VERSION_ATTRIBUTES` in the scooby knowledge base
- lookup `VERSION_METHODS` in the scooby knowledge base
`VERSION_ATTRIBUTES` is a dictionary of attributes for known python packages
with a non-standard place for the version. You can add other known places via:
```py
scooby.knowledge.VERSION_ATTRIBUTES['a_module'] = 'Awesome_version_location'
```
Similarly, `VERSION_METHODS` is a dictionary for methods to retrieve the
version, and you can similarly add your methods which will get the version
of a package.
### Using scooby to get version information.
If you are only interested in the version of a single package then you can use
scooby as well. A few examples:
```py
>>> import scooby, numpy
>>> scooby.get_version(numpy)
('numpy', '1.16.4')
>>> scooby.get_version('no_version')
('no_version', 'Version unknown')
>>> scooby.get_version('does_not_exist')
('does_not_exist', 'Could not import')
```
Note that modules can be provided as already loaded ones or as strings.
### Tracking Imports in a Session
Scooby has the ability to track all imported modules during a Python session
such that *any* imported, non-standard lib package that is used in the session
is reported by a `TrackedReport`. For instance, start a session by importing
scooby and enabling tracking with the `track_imports()` function.
Then *all* subsequent packages that are imported during the session will be
tracked and scooby can report their versions.
Once you are ready to generate a `Report`, instantiate a `TrackedReport` object.
In the following example, we import a constant from `scipy` which will report
the versions of `scipy` and `numpy` as both packages are loaded in the session
(note that `numpy` is internally loaded by `scipy`).
```py
>>> import scooby
>>> scooby.track_imports()
>>> from scipy.constants import mu_0 # a float value
>>> scooby.TrackedReport()
```
```
--------------------------------------------------------------------------------
Date: Thu Apr 16 15:33:11 2020 MDT
OS : Linux
CPU(s) : 8
Machine : x86_64
Architecture : 64bit
RAM : 62.7 GiB
Environment : IPython
Python 3.7.7 (default, Mar 10 2020, 15:16:38) [GCC 7.5.0]
scooby : 0.5.2
numpy : 1.18.1
scipy : 1.4.1
--------------------------------------------------------------------------------
```
## Command-Line Interface
Scooby comes with a command-line interface. Simply typing
```bash
scooby
```
in a terminal will display the default report. You can also use the CLI to show
the scooby Report of another package if that package has implemented a Report
class as suggested above, using `packagename.Report()`.
As an example, to print the report of pyvista you can run
```bash
scooby -r pyvista
```
which will show the Report implemented in PyVista.
The CLI can also generate a report based on the dependencies of a package's
distribution where that package hasn't implemented a Report class. For example,
we can generate a Report for `matplotlib` and its dependencies:
```bash
$ scooby -r matplotlib
--------------------------------------------------------------------------------
Date: Fri Oct 20 17:03:45 2023 PDT
OS : Darwin
CPU(s) : 8
Machine : arm64
Architecture : 64bit
RAM : 16.0 GiB
Environment : Python
File system : apfs
Python 3.11.3 | packaged by conda-forge | (main, Apr 6 2023, 08:58:31)
[Clang 14.0.6 ]
matplotlib : 3.7.1
contourpy : 1.0.7
cycler : 0.11.0
fonttools : 4.39.4
kiwisolver : 1.4.4
numpy : 1.24.3
packaging : 23.1
pillow : 9.5.0
pyparsing : 3.0.9
python-dateutil : 2.8.2
importlib-resources : 5.12.0
--------------------------------------------------------------------------------
```
Simply type
```bash
scooby --help
```
to see all the possibilities.
## Optional Requirements
The following is a list of optional requirements and their purpose:
- `psutil`: report total RAM in GiB
- `mkl-services`: report Intel(R) Math Kernel Library version
|