This commit is contained in:
2025-09-07 22:09:54 +02:00
parent e1b817252c
commit 2fc0d000b6
7796 changed files with 2159515 additions and 933 deletions

View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2018-2025 Faculty Science Limited
Copyright 2025 the dash-bootstrap-components maintainers
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,28 @@
Copyright 2010 Pallets
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,92 @@
Metadata-Version: 2.1
Name: MarkupSafe
Version: 3.0.2
Summary: Safely add untrusted strings to HTML/XML markup.
Maintainer-email: Pallets <contact@palletsprojects.com>
License: Copyright 2010 Pallets
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Project-URL: Donate, https://palletsprojects.com/donate
Project-URL: Documentation, https://markupsafe.palletsprojects.com/
Project-URL: Changes, https://markupsafe.palletsprojects.com/changes/
Project-URL: Source, https://github.com/pallets/markupsafe/
Project-URL: Chat, https://discord.gg/pallets
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Classifier: Topic :: Text Processing :: Markup :: HTML
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE.txt
# MarkupSafe
MarkupSafe implements a text object that escapes characters so it is
safe to use in HTML and XML. Characters that have special meanings are
replaced so that they display as the actual characters. This mitigates
injection attacks, meaning untrusted user input can safely be displayed
on a page.
## Examples
```pycon
>>> from markupsafe import Markup, escape
>>> # escape replaces special characters and wraps in Markup
>>> escape("<script>alert(document.cookie);</script>")
Markup('&lt;script&gt;alert(document.cookie);&lt;/script&gt;')
>>> # wrap in Markup to mark text "safe" and prevent escaping
>>> Markup("<strong>Hello</strong>")
Markup('<strong>hello</strong>')
>>> escape(Markup("<strong>Hello</strong>"))
Markup('<strong>hello</strong>')
>>> # Markup is a str subclass
>>> # methods and operators escape their arguments
>>> template = Markup("Hello <em>{name}</em>")
>>> template.format(name='"World"')
Markup('Hello <em>&#34;World&#34;</em>')
```
## Donate
The Pallets organization develops and supports MarkupSafe and other
popular packages. In order to grow the community of contributors and
users, and allow the maintainers to devote more time to the projects,
[please donate today][].
[please donate today]: https://palletsprojects.com/donate

View File

@ -0,0 +1,13 @@
MarkupSafe-3.0.2.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
MarkupSafe-3.0.2.dist-info/LICENSE.txt,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475
MarkupSafe-3.0.2.dist-info/METADATA,sha256=aAwbZhSmXdfFuMM-rEHpeiHRkBOGESyVLJIuwzHP-nw,3975
MarkupSafe-3.0.2.dist-info/RECORD,,
MarkupSafe-3.0.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
MarkupSafe-3.0.2.dist-info/WHEEL,sha256=wZi4olA0NR6c8yfzURN7DX9ImcSoHfH-g7UT7-9uFnE,109
MarkupSafe-3.0.2.dist-info/top_level.txt,sha256=qy0Plje5IJuvsCBjejJyhDCjEAdcDLK_2agVcex8Z6U,11
markupsafe/__init__.py,sha256=sr-U6_27DfaSrj5jnHYxWN-pvhM27sjlDplMDPZKm7k,13214
markupsafe/_native.py,sha256=hSLs8Jmz5aqayuengJJ3kdT5PwNpBWpKrmQSdipndC8,210
markupsafe/_speedups.c,sha256=O7XulmTo-epI6n2FtMVOrJXl8EAaIwD2iNYmBI5SEoQ,4149
markupsafe/_speedups.cpython-311-darwin.so,sha256=NHnXuz84IzAulUxl_VDkSUlQVKrzHXMIAWxCi0e01dM,50688
markupsafe/_speedups.pyi,sha256=ENd1bYe7gbBUf2ywyYWOGUpnXOHNJ-cgTNqetlW8h5k,41
markupsafe/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0

View File

@ -0,0 +1,5 @@
Wheel-Version: 1.0
Generator: setuptools (75.2.0)
Root-Is-Purelib: false
Tag: cp311-cp311-macosx_11_0_arm64

View File

@ -0,0 +1 @@
markupsafe

View File

@ -0,0 +1,7 @@
dash-bootstrap-components
Copyright 2018-2025 Faculty Science Limited
Copyright 2025 the dash-bootstrap-components maintainers
This product includes software originally developed at Faculty Science Limited (https://faculty.ai/).
The dash-bootstrap-components logo was originally designed by Bureau Bureau (https://bureaubureau.uk/).

View File

@ -0,0 +1 @@
uv

View File

@ -0,0 +1,20 @@
Copyright (c) 2017-2021 Ingy döt Net
Copyright (c) 2006-2016 Kirill Simonov
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,46 @@
Metadata-Version: 2.1
Name: PyYAML
Version: 6.0.2
Summary: YAML parser and emitter for Python
Home-page: https://pyyaml.org/
Download-URL: https://pypi.org/project/PyYAML/
Author: Kirill Simonov
Author-email: xi@resolvent.net
License: MIT
Project-URL: Bug Tracker, https://github.com/yaml/pyyaml/issues
Project-URL: CI, https://github.com/yaml/pyyaml/actions
Project-URL: Documentation, https://pyyaml.org/wiki/PyYAMLDocumentation
Project-URL: Mailing lists, http://lists.sourceforge.net/lists/listinfo/yaml-core
Project-URL: Source Code, https://github.com/yaml/pyyaml
Platform: Any
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Cython
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Text Processing :: Markup
Requires-Python: >=3.8
License-File: LICENSE
YAML is a data serialization format designed for human readability
and interaction with scripting languages. PyYAML is a YAML parser
and emitter for Python.
PyYAML features a complete YAML 1.1 parser, Unicode support, pickle
support, capable extension API, and sensible error messages. PyYAML
supports standard YAML tags and provides Python-specific tags that
allow to represent an arbitrary Python object.
PyYAML is applicable for a broad range of tasks from complex
configuration files to object serialization and persistence.

View File

@ -0,0 +1,26 @@
PyYAML-6.0.2.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
PyYAML-6.0.2.dist-info/LICENSE,sha256=jTko-dxEkP1jVwfLiOsmvXZBAqcoKVQwfT5RZ6V36KQ,1101
PyYAML-6.0.2.dist-info/METADATA,sha256=9-odFB5seu4pGPcEv7E8iyxNF51_uKnaNGjLAhz2lto,2060
PyYAML-6.0.2.dist-info/RECORD,,
PyYAML-6.0.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
PyYAML-6.0.2.dist-info/WHEEL,sha256=LFVzND6nAdWMS-norKkn42oL86bk-j1PiLvh1xzptX0,110
PyYAML-6.0.2.dist-info/top_level.txt,sha256=rpj0IVMTisAjh_1vG3Ccf9v5jpCQwAz6cD1IVU5ZdhQ,11
_yaml/__init__.py,sha256=04Ae_5osxahpJHa3XBZUAf4wi6XX32gR8D6X6p64GEA,1402
yaml/__init__.py,sha256=N35S01HMesFTe0aRRMWkPj0Pa8IEbHpE9FK7cr5Bdtw,12311
yaml/_yaml.cpython-311-darwin.so,sha256=YdahBTjS8KitV8Lm6bzs2ON1yRZtfHTI-UguNFhwElI,359272
yaml/composer.py,sha256=_Ko30Wr6eDWUeUpauUGT3Lcg9QPBnOPVlTnIMRGJ9FM,4883
yaml/constructor.py,sha256=kNgkfaeLUkwQYY_Q6Ff1Tz2XVw_pG1xVE9Ak7z-viLA,28639
yaml/cyaml.py,sha256=6ZrAG9fAYvdVe2FK_w0hmXoG7ZYsoYUwapG8CiC72H0,3851
yaml/dumper.py,sha256=PLctZlYwZLp7XmeUdwRuv4nYOZ2UBnDIUy8-lKfLF-o,2837
yaml/emitter.py,sha256=jghtaU7eFwg31bG0B7RZea_29Adi9CKmXq_QjgQpCkQ,43006
yaml/error.py,sha256=Ah9z-toHJUbE9j-M8YpxgSRM5CgLCcwVzJgLLRF2Fxo,2533
yaml/events.py,sha256=50_TksgQiE4up-lKo_V-nBy-tAIxkIPQxY5qDhKCeHw,2445
yaml/loader.py,sha256=UVa-zIqmkFSCIYq_PgSGm4NSJttHY2Rf_zQ4_b1fHN0,2061
yaml/nodes.py,sha256=gPKNj8pKCdh2d4gr3gIYINnPOaOxGhJAUiYhGRnPE84,1440
yaml/parser.py,sha256=ilWp5vvgoHFGzvOZDItFoGjD6D42nhlZrZyjAwa0oJo,25495
yaml/reader.py,sha256=0dmzirOiDG4Xo41RnuQS7K9rkY3xjHiVasfDMNTqCNw,6794
yaml/representer.py,sha256=IuWP-cAW9sHKEnS0gCqSa894k1Bg4cgTxaDwIcbRQ-Y,14190
yaml/resolver.py,sha256=9L-VYfm4mWHxUD1Vg4X7rjDRK_7VZd6b92wzq7Y2IKY,9004
yaml/scanner.py,sha256=YEM3iLZSaQwXcQRg2l2R4MdT0zGP2F9eHkKGKnHyWQY,51279
yaml/serializer.py,sha256=ChuFgmhU01hj4xgI8GaKv6vfM2Bujwa9i7d2FAHj7cA,4165
yaml/tokens.py,sha256=lTQIzSVw8Mg9wv459-TjiOQe6wVziqaRlqX2_89rp54,2573

View File

@ -0,0 +1,5 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.44.0)
Root-Is-Purelib: false
Tag: cp311-cp311-macosx_11_0_arm64

View File

@ -0,0 +1,2 @@
_yaml
yaml

View File

@ -0,0 +1,124 @@
<p align="center">
<a href="https://www.dash-bootstrap-components.com/">
<img src="https://cdn.jsdelivr.net/gh/dbc-team/dash-bootstrap-components@main/readme-images/logo.png" alt="dash-bootstrap-components logo" width="200" height="200">
</a>
</p>
<h3 align="center">Dash Bootstrap Components</h3>
<p align="center">
Bootstrap components for Plotly Dash
<br>
<a href="https://www.dash-bootstrap-components.com/">Explore the documentation</a>
·
<a href="https://github.com/dbc-team/dash-bootstrap-components/issues/new?template=bug.md">Report a bug</a>
·
<a href="https://github.com/dbc-team/dash-bootstrap-components/issues/new?template=feature.md">Request a feature</a>
<br>
<br>
<img alt="GitHub Actions" src="https://github.com/dbc-team/dash-bootstrap-components/actions/workflows/tests.yml/badge.svg">
<img alt="GitHub" src="https://img.shields.io/github/license/dbc-team/dash-bootstrap-components">
<img alt="PyPI" src="https://img.shields.io/pypi/v/dash-bootstrap-components">
<img alt="Conda (channel only)" src="https://img.shields.io/conda/vn/conda-forge/dash-bootstrap-components">
<img alt="PyPI - Python Version" src="https://img.shields.io/pypi/pyversions/dash-bootstrap-components">
</p>
_dash-bootstrap-components_ is a library of [Bootstrap][bootstrap-homepage]
components for use with [Plotly Dash][dash-homepage], that makes it easier to
build consistently styled Dash apps with complex, responsive layouts.
## Table of contents
- [Installation](#installation)
- [Quick start](#quick-start)
- [Contributing](#contributing)
- [Copyright and license](#copyright-and-license)
## Installation
### PyPI
You can install _dash-bootstrap-components_ with `pip`:
```sh
pip install dash-bootstrap-components
```
### Anaconda
You can also install _dash-bootstrap-components_ with `conda` through the
conda-forge channel:
```sh
conda install -c conda-forge dash-bootstrap-components
```
## Quick start
To use _dash-bootstrap-components_ you must do two things:
- Link a Bootstrap v5 compatible stylesheet
- Incorporate _dash-bootstrap-components_ into your layout
### Linking a stylesheet
_dash-bootstrap-components_ doesn't come with CSS included. This is to give you
the freedom to use any Bootstrap v5 stylesheet of your choice. This means
however that in order for the components to be styled properly, you must link
to a stylesheet yourself.
For convenience, links to [BootstrapCDN][bootstrapcdn] for each theme are
available through the `themes` module, which can be used as follows:
```python
import dash
import dash_bootstrap_components as dbc
app = dash.Dash(external_stylesheets=[dbc.themes.BOOTSTRAP])
```
For more information on how to link local or external CSS, check out the
[Dash documentation][dash-docs-external].
### Build the layout
With CSS linked, you can start building your app's layout with our Bootstrap
components. These include layout components for organising the content of your app on the page, as well as UI components like navbars, cards, alerts and many more.
![layout](https://cdn.jsdelivr.net/gh/dbc-team/dash-bootstrap-components@main/readme-images/layout.png)
See our [_documentation_][docs-components] for a full list of available
components.
## Contributing
We welcome contributions to _dash-bootstrap-components_. If you find a bug or
something is unclear please [submit a bug report][bug-report], if you have ideas
for new features please feel free to make a [feature request][feature-request].
If you would like to submit a pull request, please read our
[contributing guide][contribution-guide], which contains instructions on how to
build and install _dash-bootstrap-components_ locally, how to check your code
will pass our linting checks, and how to submit the pull request itself.
## Acknowledgements
The _dash-bootstrap-components_ maintainers would like to thank [Faculty][faculty] for their early support in helping this project get off the ground. Since 2025, it has been maintained by the Dash community.
## Copyright and license
Copyright © 20182025 [Faculty Science Ltd.][faculty]
Copyright © 2025 the _dash-bootstrap-components_ maintainers
Released under the [Apache 2.0 license](https://github.com/dbc-team/dash-bootstrap-components/blob/main/LICENSE).
[dash-homepage]: https://dash.plotly.com/
[dash-docs-external]: https://dash.plotly.com/external-resources
[bootstrap-homepage]: https://getbootstrap.com/
[docs-components]: https://www.dash-bootstrap-components.com/docs/components
[bootstrapcdn]: https://www.bootstrapcdn.com/
[faculty]: https://faculty.ai
[bug-report]: https://github.com/dbc-team/dash-bootstrap-components/issues/new?template=bug.md
[feature-request]: https://github.com/dbc-team/dash-bootstrap-components/issues/new?template=feature.md
[contribution-guide]: https://github.com/dbc-team/dash-bootstrap-components/blob/main/.github/CONTRIBUTING.md

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,883 @@
"""
colors
=====
Functions that manipulate colors and arrays of colors.
-----
There are three basic types of color types: rgb, hex and tuple:
rgb - An rgb color is a string of the form 'rgb(a,b,c)' where a, b and c are
integers between 0 and 255 inclusive.
hex - A hex color is a string of the form '#xxxxxx' where each x is a
character that belongs to the set [0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f]. This is
just the set of characters used in the hexadecimal numeric system.
tuple - A tuple color is a 3-tuple of the form (a,b,c) where a, b and c are
floats between 0 and 1 inclusive.
-----
Colormaps and Colorscales:
A colormap or a colorscale is a correspondence between values - Pythonic
objects such as strings and floats - to colors.
There are typically two main types of colormaps that exist: numerical and
categorical colormaps.
Numerical:
----------
Numerical colormaps are used when the coloring column being used takes a
spectrum of values or numbers.
A classic example from the Plotly library:
```
rainbow_colorscale = [
[0, 'rgb(150,0,90)'], [0.125, 'rgb(0,0,200)'],
[0.25, 'rgb(0,25,255)'], [0.375, 'rgb(0,152,255)'],
[0.5, 'rgb(44,255,150)'], [0.625, 'rgb(151,255,0)'],
[0.75, 'rgb(255,234,0)'], [0.875, 'rgb(255,111,0)'],
[1, 'rgb(255,0,0)']
]
```
Notice that this colorscale is a list of lists with each inner list containing
a number and a color. These left hand numbers in the nested lists go from 0 to
1, and they are like pointers tell you when a number is mapped to a specific
color.
If you have a column of numbers `col_num` that you want to plot, and you know
```
min(col_num) = 0
max(col_num) = 100
```
then if you pull out the number `12.5` in the list and want to figure out what
color the corresponding chart element (bar, scatter plot, etc) is going to be,
you'll figure out that proportionally 12.5 to 100 is the same as 0.125 to 1.
So, the point will be mapped to 'rgb(0,0,200)'.
All other colors between the pinned values in a colorscale are linearly
interpolated.
Categorical:
------------
Alternatively, a categorical colormap is used to assign a specific value in a
color column to a specific color everytime it appears in the dataset.
A column of strings in a panadas.dataframe that is chosen to serve as the
color index would naturally use a categorical colormap. However, you can
choose to use a categorical colormap with a column of numbers.
Be careful! If you have a lot of unique numbers in your color column you will
end up with a colormap that is massive and may slow down graphing performance.
"""
import decimal
from numbers import Number
from _plotly_utils import exceptions
# Built-in qualitative color sequences and sequential,
# diverging and cyclical color scales.
#
# Initially ported over from plotly_express
from . import ( # noqa: F401
qualitative,
sequential,
diverging,
cyclical,
cmocean,
colorbrewer,
carto,
plotlyjs,
)
DEFAULT_PLOTLY_COLORS = [
"rgb(31, 119, 180)",
"rgb(255, 127, 14)",
"rgb(44, 160, 44)",
"rgb(214, 39, 40)",
"rgb(148, 103, 189)",
"rgb(140, 86, 75)",
"rgb(227, 119, 194)",
"rgb(127, 127, 127)",
"rgb(188, 189, 34)",
"rgb(23, 190, 207)",
]
PLOTLY_SCALES = {
"Greys": [[0, "rgb(0,0,0)"], [1, "rgb(255,255,255)"]],
"YlGnBu": [
[0, "rgb(8,29,88)"],
[0.125, "rgb(37,52,148)"],
[0.25, "rgb(34,94,168)"],
[0.375, "rgb(29,145,192)"],
[0.5, "rgb(65,182,196)"],
[0.625, "rgb(127,205,187)"],
[0.75, "rgb(199,233,180)"],
[0.875, "rgb(237,248,217)"],
[1, "rgb(255,255,217)"],
],
"Greens": [
[0, "rgb(0,68,27)"],
[0.125, "rgb(0,109,44)"],
[0.25, "rgb(35,139,69)"],
[0.375, "rgb(65,171,93)"],
[0.5, "rgb(116,196,118)"],
[0.625, "rgb(161,217,155)"],
[0.75, "rgb(199,233,192)"],
[0.875, "rgb(229,245,224)"],
[1, "rgb(247,252,245)"],
],
"YlOrRd": [
[0, "rgb(128,0,38)"],
[0.125, "rgb(189,0,38)"],
[0.25, "rgb(227,26,28)"],
[0.375, "rgb(252,78,42)"],
[0.5, "rgb(253,141,60)"],
[0.625, "rgb(254,178,76)"],
[0.75, "rgb(254,217,118)"],
[0.875, "rgb(255,237,160)"],
[1, "rgb(255,255,204)"],
],
"Bluered": [[0, "rgb(0,0,255)"], [1, "rgb(255,0,0)"]],
# modified RdBu based on
# www.sandia.gov/~kmorel/documents/ColorMaps/ColorMapsExpanded.pdf
"RdBu": [
[0, "rgb(5,10,172)"],
[0.35, "rgb(106,137,247)"],
[0.5, "rgb(190,190,190)"],
[0.6, "rgb(220,170,132)"],
[0.7, "rgb(230,145,90)"],
[1, "rgb(178,10,28)"],
],
# Scale for non-negative numeric values
"Reds": [
[0, "rgb(220,220,220)"],
[0.2, "rgb(245,195,157)"],
[0.4, "rgb(245,160,105)"],
[1, "rgb(178,10,28)"],
],
# Scale for non-positive numeric values
"Blues": [
[0, "rgb(5,10,172)"],
[0.35, "rgb(40,60,190)"],
[0.5, "rgb(70,100,245)"],
[0.6, "rgb(90,120,245)"],
[0.7, "rgb(106,137,247)"],
[1, "rgb(220,220,220)"],
],
"Picnic": [
[0, "rgb(0,0,255)"],
[0.1, "rgb(51,153,255)"],
[0.2, "rgb(102,204,255)"],
[0.3, "rgb(153,204,255)"],
[0.4, "rgb(204,204,255)"],
[0.5, "rgb(255,255,255)"],
[0.6, "rgb(255,204,255)"],
[0.7, "rgb(255,153,255)"],
[0.8, "rgb(255,102,204)"],
[0.9, "rgb(255,102,102)"],
[1, "rgb(255,0,0)"],
],
"Rainbow": [
[0, "rgb(150,0,90)"],
[0.125, "rgb(0,0,200)"],
[0.25, "rgb(0,25,255)"],
[0.375, "rgb(0,152,255)"],
[0.5, "rgb(44,255,150)"],
[0.625, "rgb(151,255,0)"],
[0.75, "rgb(255,234,0)"],
[0.875, "rgb(255,111,0)"],
[1, "rgb(255,0,0)"],
],
"Portland": [
[0, "rgb(12,51,131)"],
[0.25, "rgb(10,136,186)"],
[0.5, "rgb(242,211,56)"],
[0.75, "rgb(242,143,56)"],
[1, "rgb(217,30,30)"],
],
"Jet": [
[0, "rgb(0,0,131)"],
[0.125, "rgb(0,60,170)"],
[0.375, "rgb(5,255,255)"],
[0.625, "rgb(255,255,0)"],
[0.875, "rgb(250,0,0)"],
[1, "rgb(128,0,0)"],
],
"Hot": [
[0, "rgb(0,0,0)"],
[0.3, "rgb(230,0,0)"],
[0.6, "rgb(255,210,0)"],
[1, "rgb(255,255,255)"],
],
"Blackbody": [
[0, "rgb(0,0,0)"],
[0.2, "rgb(230,0,0)"],
[0.4, "rgb(230,210,0)"],
[0.7, "rgb(255,255,255)"],
[1, "rgb(160,200,255)"],
],
"Earth": [
[0, "rgb(0,0,130)"],
[0.1, "rgb(0,180,180)"],
[0.2, "rgb(40,210,40)"],
[0.4, "rgb(230,230,50)"],
[0.6, "rgb(120,70,20)"],
[1, "rgb(255,255,255)"],
],
"Electric": [
[0, "rgb(0,0,0)"],
[0.15, "rgb(30,0,100)"],
[0.4, "rgb(120,0,100)"],
[0.6, "rgb(160,90,0)"],
[0.8, "rgb(230,200,0)"],
[1, "rgb(255,250,220)"],
],
"Viridis": [
[0, "#440154"],
[0.06274509803921569, "#48186a"],
[0.12549019607843137, "#472d7b"],
[0.18823529411764706, "#424086"],
[0.25098039215686274, "#3b528b"],
[0.3137254901960784, "#33638d"],
[0.3764705882352941, "#2c728e"],
[0.4392156862745098, "#26828e"],
[0.5019607843137255, "#21918c"],
[0.5647058823529412, "#1fa088"],
[0.6274509803921569, "#28ae80"],
[0.6901960784313725, "#3fbc73"],
[0.7529411764705882, "#5ec962"],
[0.8156862745098039, "#84d44b"],
[0.8784313725490196, "#addc30"],
[0.9411764705882353, "#d8e219"],
[1, "#fde725"],
],
"Cividis": [
[0.000000, "rgb(0,32,76)"],
[0.058824, "rgb(0,42,102)"],
[0.117647, "rgb(0,52,110)"],
[0.176471, "rgb(39,63,108)"],
[0.235294, "rgb(60,74,107)"],
[0.294118, "rgb(76,85,107)"],
[0.352941, "rgb(91,95,109)"],
[0.411765, "rgb(104,106,112)"],
[0.470588, "rgb(117,117,117)"],
[0.529412, "rgb(131,129,120)"],
[0.588235, "rgb(146,140,120)"],
[0.647059, "rgb(161,152,118)"],
[0.705882, "rgb(176,165,114)"],
[0.764706, "rgb(192,177,109)"],
[0.823529, "rgb(209,191,102)"],
[0.882353, "rgb(225,204,92)"],
[0.941176, "rgb(243,219,79)"],
[1.000000, "rgb(255,233,69)"],
],
}
def color_parser(colors, function):
"""
Takes color(s) and a function and applies the function on the color(s)
In particular, this function identifies whether the given color object
is an iterable or not and applies the given color-parsing function to
the color or iterable of colors. If given an iterable, it will only be
able to work with it if all items in the iterable are of the same type
- rgb string, hex string or tuple
"""
if isinstance(colors, str):
return function(colors)
if isinstance(colors, tuple) and isinstance(colors[0], Number):
return function(colors)
if hasattr(colors, "__iter__"):
if isinstance(colors, tuple):
new_color_tuple = tuple(function(item) for item in colors)
return new_color_tuple
else:
new_color_list = [function(item) for item in colors]
return new_color_list
def validate_colors(colors, colortype="tuple"):
"""
Validates color(s) and returns a list of color(s) of a specified type
"""
from numbers import Number
if colors is None:
colors = DEFAULT_PLOTLY_COLORS
if isinstance(colors, str):
if colors in PLOTLY_SCALES:
colors_list = colorscale_to_colors(PLOTLY_SCALES[colors])
# TODO: fix _gantt.py/_scatter.py so that they can accept the
# actual colorscale and not just a list of the first and last
# color in the plotly colorscale. In resolving this issue we
# will be removing the immediate line below
colors = [colors_list[0]] + [colors_list[-1]]
elif "rgb" in colors or "#" in colors:
colors = [colors]
else:
raise exceptions.PlotlyError(
"If your colors variable is a string, it must be a "
"Plotly scale, an rgb color or a hex color."
)
elif isinstance(colors, tuple):
if isinstance(colors[0], Number):
colors = [colors]
else:
colors = list(colors)
# convert color elements in list to tuple color
for j, each_color in enumerate(colors):
if "rgb" in each_color:
each_color = color_parser(each_color, unlabel_rgb)
for value in each_color:
if value > 255.0:
raise exceptions.PlotlyError(
"Whoops! The elements in your rgb colors "
"tuples cannot exceed 255.0."
)
each_color = color_parser(each_color, unconvert_from_RGB_255)
colors[j] = each_color
if "#" in each_color:
each_color = color_parser(each_color, hex_to_rgb)
each_color = color_parser(each_color, unconvert_from_RGB_255)
colors[j] = each_color
if isinstance(each_color, tuple):
for value in each_color:
if value > 1.0:
raise exceptions.PlotlyError(
"Whoops! The elements in your colors tuples cannot exceed 1.0."
)
colors[j] = each_color
if colortype == "rgb" and not isinstance(colors, str):
for j, each_color in enumerate(colors):
rgb_color = color_parser(each_color, convert_to_RGB_255)
colors[j] = color_parser(rgb_color, label_rgb)
return colors
def validate_colors_dict(colors, colortype="tuple"):
"""
Validates dictionary of color(s)
"""
# validate each color element in the dictionary
for key in colors:
if "rgb" in colors[key]:
colors[key] = color_parser(colors[key], unlabel_rgb)
for value in colors[key]:
if value > 255.0:
raise exceptions.PlotlyError(
"Whoops! The elements in your rgb colors "
"tuples cannot exceed 255.0."
)
colors[key] = color_parser(colors[key], unconvert_from_RGB_255)
if "#" in colors[key]:
colors[key] = color_parser(colors[key], hex_to_rgb)
colors[key] = color_parser(colors[key], unconvert_from_RGB_255)
if isinstance(colors[key], tuple):
for value in colors[key]:
if value > 1.0:
raise exceptions.PlotlyError(
"Whoops! The elements in your colors tuples cannot exceed 1.0."
)
if colortype == "rgb":
for key in colors:
colors[key] = color_parser(colors[key], convert_to_RGB_255)
colors[key] = color_parser(colors[key], label_rgb)
return colors
def convert_colors_to_same_type(
colors,
colortype="rgb",
scale=None,
return_default_colors=False,
num_of_defualt_colors=2,
):
"""
Converts color(s) to the specified color type
Takes a single color or an iterable of colors, as well as a list of scale
values, and outputs a 2-pair of the list of color(s) converted all to an
rgb or tuple color type, aswell as the scale as the second element. If
colors is a Plotly Scale name, then 'scale' will be forced to the scale
from the respective colorscale and the colors in that colorscale will also
be coverted to the selected colortype. If colors is None, then there is an
option to return portion of the DEFAULT_PLOTLY_COLORS
:param (str|tuple|list) colors: either a plotly scale name, an rgb or hex
color, a color tuple or a list/tuple of colors
:param (list) scale: see docs for validate_scale_values()
:rtype (tuple) (colors_list, scale) if scale is None in the function call,
then scale will remain None in the returned tuple
"""
colors_list = []
if colors is None and return_default_colors is True:
colors_list = DEFAULT_PLOTLY_COLORS[0:num_of_defualt_colors]
if isinstance(colors, str):
if colors in PLOTLY_SCALES:
colors_list = colorscale_to_colors(PLOTLY_SCALES[colors])
if scale is None:
scale = colorscale_to_scale(PLOTLY_SCALES[colors])
elif "rgb" in colors or "#" in colors:
colors_list = [colors]
elif isinstance(colors, tuple):
if isinstance(colors[0], Number):
colors_list = [colors]
else:
colors_list = list(colors)
elif isinstance(colors, list):
colors_list = colors
# validate scale
if scale is not None:
validate_scale_values(scale)
if len(colors_list) != len(scale):
raise exceptions.PlotlyError(
"Make sure that the length of your scale matches the length "
"of your list of colors which is {}.".format(len(colors_list))
)
# convert all colors to rgb
for j, each_color in enumerate(colors_list):
if "#" in each_color:
each_color = color_parser(each_color, hex_to_rgb)
each_color = color_parser(each_color, label_rgb)
colors_list[j] = each_color
elif isinstance(each_color, tuple):
each_color = color_parser(each_color, convert_to_RGB_255)
each_color = color_parser(each_color, label_rgb)
colors_list[j] = each_color
if colortype == "rgb":
return (colors_list, scale)
elif colortype == "tuple":
for j, each_color in enumerate(colors_list):
each_color = color_parser(each_color, unlabel_rgb)
each_color = color_parser(each_color, unconvert_from_RGB_255)
colors_list[j] = each_color
return (colors_list, scale)
else:
raise exceptions.PlotlyError(
"You must select either rgb or tuple for your colortype variable."
)
def convert_dict_colors_to_same_type(colors_dict, colortype="rgb"):
"""
Converts a colors in a dictionary of colors to the specified color type
:param (dict) colors_dict: a dictionary whose values are single colors
"""
for key in colors_dict:
if "#" in colors_dict[key]:
colors_dict[key] = color_parser(colors_dict[key], hex_to_rgb)
colors_dict[key] = color_parser(colors_dict[key], label_rgb)
elif isinstance(colors_dict[key], tuple):
colors_dict[key] = color_parser(colors_dict[key], convert_to_RGB_255)
colors_dict[key] = color_parser(colors_dict[key], label_rgb)
if colortype == "rgb":
return colors_dict
elif colortype == "tuple":
for key in colors_dict:
colors_dict[key] = color_parser(colors_dict[key], unlabel_rgb)
colors_dict[key] = color_parser(colors_dict[key], unconvert_from_RGB_255)
return colors_dict
else:
raise exceptions.PlotlyError(
"You must select either rgb or tuple for your colortype variable."
)
def validate_scale_values(scale):
"""
Validates scale values from a colorscale
:param (list) scale: a strictly increasing list of floats that begins
with 0 and ends with 1. Its usage derives from a colorscale which is
a list of two-lists (a list with two elements) of the form
[value, color] which are used to determine how interpolation weighting
works between the colors in the colorscale. Therefore scale is just
the extraction of these values from the two-lists in order
"""
if len(scale) < 2:
raise exceptions.PlotlyError(
"You must input a list of scale values that has at least two values."
)
if (scale[0] != 0) or (scale[-1] != 1):
raise exceptions.PlotlyError(
"The first and last number in your scale must be 0.0 and 1.0 respectively."
)
if not all(x < y for x, y in zip(scale, scale[1:])):
raise exceptions.PlotlyError(
"'scale' must be a list that contains a strictly increasing "
"sequence of numbers."
)
def validate_colorscale(colorscale):
"""Validate the structure, scale values and colors of colorscale."""
if not isinstance(colorscale, list):
# TODO Write tests for these exceptions
raise exceptions.PlotlyError("A valid colorscale must be a list.")
if not all(isinstance(innerlist, list) for innerlist in colorscale):
raise exceptions.PlotlyError("A valid colorscale must be a list of lists.")
colorscale_colors = colorscale_to_colors(colorscale)
scale_values = colorscale_to_scale(colorscale)
validate_scale_values(scale_values)
validate_colors(colorscale_colors)
def make_colorscale(colors, scale=None):
"""
Makes a colorscale from a list of colors and a scale
Takes a list of colors and scales and constructs a colorscale based
on the colors in sequential order. If 'scale' is left empty, a linear-
interpolated colorscale will be generated. If 'scale' is a specificed
list, it must be the same legnth as colors and must contain all floats
For documentation regarding to the form of the output, see
https://plot.ly/python/reference/#mesh3d-colorscale
:param (list) colors: a list of single colors
"""
colorscale = []
# validate minimum colors length of 2
if len(colors) < 2:
raise exceptions.PlotlyError(
"You must input a list of colors that has at least two colors."
)
if scale is None:
scale_incr = 1.0 / (len(colors) - 1)
return [[i * scale_incr, color] for i, color in enumerate(colors)]
else:
if len(colors) != len(scale):
raise exceptions.PlotlyError(
"The length of colors and scale must be the same."
)
validate_scale_values(scale)
colorscale = [list(tup) for tup in zip(scale, colors)]
return colorscale
def find_intermediate_color(lowcolor, highcolor, intermed, colortype="tuple"):
"""
Returns the color at a given distance between two colors
This function takes two color tuples, where each element is between 0
and 1, along with a value 0 < intermed < 1 and returns a color that is
intermed-percent from lowcolor to highcolor. If colortype is set to 'rgb',
the function will automatically convert the rgb type to a tuple, find the
intermediate color and return it as an rgb color.
"""
if colortype == "rgb":
# convert to tuple color, eg. (1, 0.45, 0.7)
lowcolor = unlabel_rgb(lowcolor)
highcolor = unlabel_rgb(highcolor)
diff_0 = float(highcolor[0] - lowcolor[0])
diff_1 = float(highcolor[1] - lowcolor[1])
diff_2 = float(highcolor[2] - lowcolor[2])
inter_med_tuple = (
lowcolor[0] + intermed * diff_0,
lowcolor[1] + intermed * diff_1,
lowcolor[2] + intermed * diff_2,
)
if colortype == "rgb":
# back to an rgb string, e.g. rgb(30, 20, 10)
inter_med_rgb = label_rgb(inter_med_tuple)
return inter_med_rgb
return inter_med_tuple
def unconvert_from_RGB_255(colors):
"""
Return a tuple where each element gets divided by 255
Takes a (list of) color tuple(s) where each element is between 0 and
255. Returns the same tuples where each tuple element is normalized to
a value between 0 and 1
"""
return (colors[0] / (255.0), colors[1] / (255.0), colors[2] / (255.0))
def convert_to_RGB_255(colors):
"""
Multiplies each element of a triplet by 255
Each coordinate of the color tuple is rounded to the nearest float and
then is turned into an integer. If a number is of the form x.5, then
if x is odd, the number rounds up to (x+1). Otherwise, it rounds down
to just x. This is the way rounding works in Python 3 and in current
statistical analysis to avoid rounding bias
:param (list) rgb_components: grabs the three R, G and B values to be
returned as computed in the function
"""
rgb_components = []
for component in colors:
rounded_num = decimal.Decimal(str(component * 255.0)).quantize(
decimal.Decimal("1"), rounding=decimal.ROUND_HALF_EVEN
)
# convert rounded number to an integer from 'Decimal' form
rounded_num = int(rounded_num)
rgb_components.append(rounded_num)
return (rgb_components[0], rgb_components[1], rgb_components[2])
def n_colors(lowcolor, highcolor, n_colors, colortype="tuple"):
"""
Splits a low and high color into a list of n_colors colors in it
Accepts two color tuples and returns a list of n_colors colors
which form the intermediate colors between lowcolor and highcolor
from linearly interpolating through RGB space. If colortype is 'rgb'
the function will return a list of colors in the same form.
"""
if colortype == "rgb":
# convert to tuple
lowcolor = unlabel_rgb(lowcolor)
highcolor = unlabel_rgb(highcolor)
diff_0 = float(highcolor[0] - lowcolor[0])
incr_0 = diff_0 / (n_colors - 1)
diff_1 = float(highcolor[1] - lowcolor[1])
incr_1 = diff_1 / (n_colors - 1)
diff_2 = float(highcolor[2] - lowcolor[2])
incr_2 = diff_2 / (n_colors - 1)
list_of_colors = []
def _constrain_color(c):
if c > 255.0:
return 255.0
elif c < 0.0:
return 0.0
else:
return c
for index in range(n_colors):
new_tuple = (
_constrain_color(lowcolor[0] + (index * incr_0)),
_constrain_color(lowcolor[1] + (index * incr_1)),
_constrain_color(lowcolor[2] + (index * incr_2)),
)
list_of_colors.append(new_tuple)
if colortype == "rgb":
# back to an rgb string
list_of_colors = color_parser(list_of_colors, label_rgb)
return list_of_colors
def label_rgb(colors):
"""
Takes tuple (a, b, c) and returns an rgb color 'rgb(a, b, c)'
"""
return "rgb(%s, %s, %s)" % (colors[0], colors[1], colors[2])
def unlabel_rgb(colors):
"""
Takes rgb color(s) 'rgb(a, b, c)' and returns tuple(s) (a, b, c)
This function takes either an 'rgb(a, b, c)' color or a list of
such colors and returns the color tuples in tuple(s) (a, b, c)
"""
str_vals = ""
for index in range(len(colors)):
try:
float(colors[index])
str_vals = str_vals + colors[index]
except ValueError:
if colors[index] == "," or colors[index] == ".":
str_vals = str_vals + colors[index]
str_vals = str_vals + ","
numbers = []
str_num = ""
for char in str_vals:
if char != ",":
str_num = str_num + char
else:
numbers.append(float(str_num))
str_num = ""
return (numbers[0], numbers[1], numbers[2])
def hex_to_rgb(value):
"""
Calculates rgb values from a hex color code.
:param (string) value: Hex color string
:rtype (tuple) (r_value, g_value, b_value): tuple of rgb values
"""
value = value.lstrip("#")
hex_total_length = len(value)
rgb_section_length = hex_total_length // 3
return tuple(
int(value[i : i + rgb_section_length], 16)
for i in range(0, hex_total_length, rgb_section_length)
)
def colorscale_to_colors(colorscale):
"""
Extracts the colors from colorscale as a list
"""
color_list = []
for item in colorscale:
color_list.append(item[1])
return color_list
def colorscale_to_scale(colorscale):
"""
Extracts the interpolation scale values from colorscale as a list
"""
scale_list = []
for item in colorscale:
scale_list.append(item[0])
return scale_list
def convert_colorscale_to_rgb(colorscale):
"""
Converts the colors in a colorscale to rgb colors
A colorscale is an array of arrays, each with a numeric value as the
first item and a color as the second. This function specifically is
converting a colorscale with tuple colors (each coordinate between 0
and 1) into a colorscale with the colors transformed into rgb colors
"""
for color in colorscale:
color[1] = convert_to_RGB_255(color[1])
for color in colorscale:
color[1] = label_rgb(color[1])
return colorscale
def named_colorscales():
"""
Returns lowercased names of built-in continuous colorscales.
"""
from _plotly_utils.basevalidators import ColorscaleValidator
return [c for c in ColorscaleValidator("", "").named_colorscales]
def get_colorscale(name):
"""
Returns the colorscale for a given name. See `named_colorscales` for the
built-in colorscales.
"""
from _plotly_utils.basevalidators import ColorscaleValidator
if not isinstance(name, str):
raise exceptions.PlotlyError("Name argument have to be a string.")
name = name.lower()
if name[-2:] == "_r":
should_reverse = True
name = name[:-2]
else:
should_reverse = False
if name in ColorscaleValidator("", "").named_colorscales:
colorscale = ColorscaleValidator("", "").named_colorscales[name]
else:
raise exceptions.PlotlyError(f"Colorscale {name} is not a built-in scale.")
if should_reverse:
colorscale = colorscale[::-1]
return make_colorscale(colorscale)
def sample_colorscale(colorscale, samplepoints, low=0.0, high=1.0, colortype="rgb"):
"""
Samples a colorscale at specific points.
Interpolates between colors in a colorscale to find the specific colors
corresponding to the specified sample values. The colorscale can be specified
as a list of `[scale, color]` pairs, as a list of colors, or as a named
plotly colorscale. The samplepoints can be specefied as an iterable of specific
points in the range [0.0, 1.0], or as an integer number of points which will
be spaced equally between the low value (default 0.0) and the high value
(default 1.0). The output is a list of colors, formatted according to the
specified colortype.
"""
from bisect import bisect_left
try:
validate_colorscale(colorscale)
except exceptions.PlotlyError:
if isinstance(colorscale, str):
colorscale = get_colorscale(colorscale)
else:
colorscale = make_colorscale(colorscale)
scale = colorscale_to_scale(colorscale)
validate_scale_values(scale)
colors = colorscale_to_colors(colorscale)
colors = validate_colors(colors, colortype="tuple")
if isinstance(samplepoints, int):
samplepoints = [
low + idx / (samplepoints - 1) * (high - low) for idx in range(samplepoints)
]
elif isinstance(samplepoints, float):
samplepoints = [samplepoints]
sampled_colors = []
for point in samplepoints:
high = bisect_left(scale, point)
low = high - 1
interpolant = (point - scale[low]) / (scale[high] - scale[low])
sampled_color = find_intermediate_color(colors[low], colors[high], interpolant)
sampled_colors.append(sampled_color)
return validate_colors(sampled_colors, colortype=colortype)

View File

@ -0,0 +1,161 @@
def _swatches(module_names, module_contents, template=None):
"""
Parameters
----------
template : str or dict or plotly.graph_objects.layout.Template instance
The figure template name or definition.
Returns
-------
fig : graph_objects.Figure containing the displayed image
A `Figure` object. This figure demonstrates the color scales and
sequences in this module, as stacked bar charts.
"""
import plotly.graph_objs as go
from plotly.express._core import apply_default_cascade
args = dict(template=template)
apply_default_cascade(args)
sequences = [
(k, v)
for k, v in module_contents.items()
if not (k.startswith("_") or k.startswith("swatches") or k.endswith("_r"))
]
return go.Figure(
data=[
go.Bar(
orientation="h",
y=[name] * len(colors),
x=[1] * len(colors),
customdata=list(range(len(colors))),
marker=dict(color=colors),
hovertemplate="%{y}[%{customdata}] = %{marker.color}<extra></extra>",
)
for name, colors in reversed(sequences)
],
layout=dict(
title="plotly.colors." + module_names.split(".")[-1],
barmode="stack",
barnorm="fraction",
bargap=0.5,
showlegend=False,
xaxis=dict(range=[-0.02, 1.02], showticklabels=False, showgrid=False),
height=max(600, 40 * len(sequences)),
template=args["template"],
margin=dict(b=10),
),
)
def _swatches_continuous(module_names, module_contents, template=None):
"""
Parameters
----------
template : str or dict or plotly.graph_objects.layout.Template instance
The figure template name or definition.
Returns
-------
fig : graph_objects.Figure containing the displayed image
A `Figure` object. This figure demonstrates the color scales and
sequences in this module, as stacked bar charts.
"""
import plotly.graph_objs as go
from plotly.express._core import apply_default_cascade
args = dict(template=template)
apply_default_cascade(args)
sequences = [
(k, v)
for k, v in module_contents.items()
if not (k.startswith("_") or k.startswith("swatches") or k.endswith("_r"))
]
n = 100
return go.Figure(
data=[
go.Bar(
orientation="h",
y=[name] * n,
x=[1] * n,
customdata=[(x + 1) / n for x in range(n)],
marker=dict(color=list(range(n)), colorscale=name, line_width=0),
hovertemplate="%{customdata}",
name=name,
)
for name, colors in reversed(sequences)
],
layout=dict(
title="plotly.colors." + module_names.split(".")[-1],
barmode="stack",
barnorm="fraction",
bargap=0.3,
showlegend=False,
xaxis=dict(range=[-0.02, 1.02], showticklabels=False, showgrid=False),
height=max(600, 40 * len(sequences)),
width=500,
template=args["template"],
margin=dict(b=10),
),
)
def _swatches_cyclical(module_names, module_contents, template=None):
"""
Parameters
----------
template : str or dict or plotly.graph_objects.layout.Template instance
The figure template name or definition.
Returns
-------
fig : graph_objects.Figure containing the displayed image
A `Figure` object. This figure demonstrates the color scales and
sequences in this module, as polar bar charts.
"""
import plotly.graph_objects as go
from plotly.subplots import make_subplots
from plotly.express._core import apply_default_cascade
args = dict(template=template)
apply_default_cascade(args)
rows = 2
cols = 4
scales = [
(k, v)
for k, v in module_contents.items()
if not (k.startswith("_") or k.startswith("swatches") or k.endswith("_r"))
]
names = [name for name, colors in scales]
fig = make_subplots(
rows=rows,
cols=cols,
subplot_titles=names,
specs=[[{"type": "polar"}] * cols] * rows,
)
for i, (name, scale) in enumerate(scales):
fig.add_trace(
go.Barpolar(
r=[1] * int(360 / 5),
theta=list(range(0, 360, 5)),
marker_color=list(range(0, 360, 5)),
marker_cmin=0,
marker_cmax=360,
marker_colorscale=name,
name=name,
),
row=int(i / cols) + 1,
col=i % cols + 1,
)
fig.update_traces(width=5.2, marker_line_width=0, base=0.5, showlegend=False)
fig.update_polars(angularaxis_visible=False, radialaxis_visible=False)
fig.update_layout(
title="plotly.colors." + module_names.split(".")[-1], template=args["template"]
)
return fig

View File

@ -0,0 +1,419 @@
"""
Color sequences and scales from CARTO's CartoColors
Learn more at https://github.com/CartoDB/CartoColor
CARTOColors are made available under a Creative Commons Attribution license: https://creativecommons.org/licenses/by/3.0/us/
"""
from ._swatches import _swatches
def swatches(template=None):
return _swatches(__name__, globals(), template)
swatches.__doc__ = _swatches.__doc__
Burg = [
"rgb(255, 198, 196)",
"rgb(244, 163, 168)",
"rgb(227, 129, 145)",
"rgb(204, 96, 125)",
"rgb(173, 70, 108)",
"rgb(139, 48, 88)",
"rgb(103, 32, 68)",
]
Burgyl = [
"rgb(251, 230, 197)",
"rgb(245, 186, 152)",
"rgb(238, 138, 130)",
"rgb(220, 113, 118)",
"rgb(200, 88, 108)",
"rgb(156, 63, 93)",
"rgb(112, 40, 74)",
]
Redor = [
"rgb(246, 210, 169)",
"rgb(245, 183, 142)",
"rgb(241, 156, 124)",
"rgb(234, 129, 113)",
"rgb(221, 104, 108)",
"rgb(202, 82, 104)",
"rgb(177, 63, 100)",
]
Oryel = [
"rgb(236, 218, 154)",
"rgb(239, 196, 126)",
"rgb(243, 173, 106)",
"rgb(247, 148, 93)",
"rgb(249, 123, 87)",
"rgb(246, 99, 86)",
"rgb(238, 77, 90)",
]
Peach = [
"rgb(253, 224, 197)",
"rgb(250, 203, 166)",
"rgb(248, 181, 139)",
"rgb(245, 158, 114)",
"rgb(242, 133, 93)",
"rgb(239, 106, 76)",
"rgb(235, 74, 64)",
]
Pinkyl = [
"rgb(254, 246, 181)",
"rgb(255, 221, 154)",
"rgb(255, 194, 133)",
"rgb(255, 166, 121)",
"rgb(250, 138, 118)",
"rgb(241, 109, 122)",
"rgb(225, 83, 131)",
]
Mint = [
"rgb(228, 241, 225)",
"rgb(180, 217, 204)",
"rgb(137, 192, 182)",
"rgb(99, 166, 160)",
"rgb(68, 140, 138)",
"rgb(40, 114, 116)",
"rgb(13, 88, 95)",
]
Blugrn = [
"rgb(196, 230, 195)",
"rgb(150, 210, 164)",
"rgb(109, 188, 144)",
"rgb(77, 162, 132)",
"rgb(54, 135, 122)",
"rgb(38, 107, 110)",
"rgb(29, 79, 96)",
]
Darkmint = [
"rgb(210, 251, 212)",
"rgb(165, 219, 194)",
"rgb(123, 188, 176)",
"rgb(85, 156, 158)",
"rgb(58, 124, 137)",
"rgb(35, 93, 114)",
"rgb(18, 63, 90)",
]
Emrld = [
"rgb(211, 242, 163)",
"rgb(151, 225, 150)",
"rgb(108, 192, 139)",
"rgb(76, 155, 130)",
"rgb(33, 122, 121)",
"rgb(16, 89, 101)",
"rgb(7, 64, 80)",
]
Aggrnyl = [
"rgb(36, 86, 104)",
"rgb(15, 114, 121)",
"rgb(13, 143, 129)",
"rgb(57, 171, 126)",
"rgb(110, 197, 116)",
"rgb(169, 220, 103)",
"rgb(237, 239, 93)",
]
Bluyl = [
"rgb(247, 254, 174)",
"rgb(183, 230, 165)",
"rgb(124, 203, 162)",
"rgb(70, 174, 160)",
"rgb(8, 144, 153)",
"rgb(0, 113, 139)",
"rgb(4, 82, 117)",
]
Teal = [
"rgb(209, 238, 234)",
"rgb(168, 219, 217)",
"rgb(133, 196, 201)",
"rgb(104, 171, 184)",
"rgb(79, 144, 166)",
"rgb(59, 115, 143)",
"rgb(42, 86, 116)",
]
Tealgrn = [
"rgb(176, 242, 188)",
"rgb(137, 232, 172)",
"rgb(103, 219, 165)",
"rgb(76, 200, 163)",
"rgb(56, 178, 163)",
"rgb(44, 152, 160)",
"rgb(37, 125, 152)",
]
Purp = [
"rgb(243, 224, 247)",
"rgb(228, 199, 241)",
"rgb(209, 175, 232)",
"rgb(185, 152, 221)",
"rgb(159, 130, 206)",
"rgb(130, 109, 186)",
"rgb(99, 88, 159)",
]
Purpor = [
"rgb(249, 221, 218)",
"rgb(242, 185, 196)",
"rgb(229, 151, 185)",
"rgb(206, 120, 179)",
"rgb(173, 95, 173)",
"rgb(131, 75, 160)",
"rgb(87, 59, 136)",
]
Sunset = [
"rgb(243, 231, 155)",
"rgb(250, 196, 132)",
"rgb(248, 160, 126)",
"rgb(235, 127, 134)",
"rgb(206, 102, 147)",
"rgb(160, 89, 160)",
"rgb(92, 83, 165)",
]
Magenta = [
"rgb(243, 203, 211)",
"rgb(234, 169, 189)",
"rgb(221, 136, 172)",
"rgb(202, 105, 157)",
"rgb(177, 77, 142)",
"rgb(145, 53, 125)",
"rgb(108, 33, 103)",
]
Sunsetdark = [
"rgb(252, 222, 156)",
"rgb(250, 164, 118)",
"rgb(240, 116, 110)",
"rgb(227, 79, 111)",
"rgb(220, 57, 119)",
"rgb(185, 37, 122)",
"rgb(124, 29, 111)",
]
Agsunset = [
"rgb(75, 41, 145)",
"rgb(135, 44, 162)",
"rgb(192, 54, 157)",
"rgb(234, 79, 136)",
"rgb(250, 120, 118)",
"rgb(246, 169, 122)",
"rgb(237, 217, 163)",
]
Brwnyl = [
"rgb(237, 229, 207)",
"rgb(224, 194, 162)",
"rgb(211, 156, 131)",
"rgb(193, 118, 111)",
"rgb(166, 84, 97)",
"rgb(129, 55, 83)",
"rgb(84, 31, 63)",
]
# Diverging schemes
Armyrose = [
"rgb(121, 130, 52)",
"rgb(163, 173, 98)",
"rgb(208, 211, 162)",
"rgb(253, 251, 228)",
"rgb(240, 198, 195)",
"rgb(223, 145, 163)",
"rgb(212, 103, 128)",
]
Fall = [
"rgb(61, 89, 65)",
"rgb(119, 136, 104)",
"rgb(181, 185, 145)",
"rgb(246, 237, 189)",
"rgb(237, 187, 138)",
"rgb(222, 138, 90)",
"rgb(202, 86, 44)",
]
Geyser = [
"rgb(0, 128, 128)",
"rgb(112, 164, 148)",
"rgb(180, 200, 168)",
"rgb(246, 237, 189)",
"rgb(237, 187, 138)",
"rgb(222, 138, 90)",
"rgb(202, 86, 44)",
]
Temps = [
"rgb(0, 147, 146)",
"rgb(57, 177, 133)",
"rgb(156, 203, 134)",
"rgb(233, 226, 156)",
"rgb(238, 180, 121)",
"rgb(232, 132, 113)",
"rgb(207, 89, 126)",
]
Tealrose = [
"rgb(0, 147, 146)",
"rgb(114, 170, 161)",
"rgb(177, 199, 179)",
"rgb(241, 234, 200)",
"rgb(229, 185, 173)",
"rgb(217, 137, 148)",
"rgb(208, 88, 126)",
]
Tropic = [
"rgb(0, 155, 158)",
"rgb(66, 183, 185)",
"rgb(167, 211, 212)",
"rgb(241, 241, 241)",
"rgb(228, 193, 217)",
"rgb(214, 145, 193)",
"rgb(199, 93, 171)",
]
Earth = [
"rgb(161, 105, 40)",
"rgb(189, 146, 90)",
"rgb(214, 189, 141)",
"rgb(237, 234, 194)",
"rgb(181, 200, 184)",
"rgb(121, 167, 172)",
"rgb(40, 135, 161)",
]
# Qualitative palettes
Antique = [
"rgb(133, 92, 117)",
"rgb(217, 175, 107)",
"rgb(175, 100, 88)",
"rgb(115, 111, 76)",
"rgb(82, 106, 131)",
"rgb(98, 83, 119)",
"rgb(104, 133, 92)",
"rgb(156, 156, 94)",
"rgb(160, 97, 119)",
"rgb(140, 120, 93)",
"rgb(124, 124, 124)",
]
Bold = [
"rgb(127, 60, 141)",
"rgb(17, 165, 121)",
"rgb(57, 105, 172)",
"rgb(242, 183, 1)",
"rgb(231, 63, 116)",
"rgb(128, 186, 90)",
"rgb(230, 131, 16)",
"rgb(0, 134, 149)",
"rgb(207, 28, 144)",
"rgb(249, 123, 114)",
"rgb(165, 170, 153)",
]
Pastel = [
"rgb(102, 197, 204)",
"rgb(246, 207, 113)",
"rgb(248, 156, 116)",
"rgb(220, 176, 242)",
"rgb(135, 197, 95)",
"rgb(158, 185, 243)",
"rgb(254, 136, 177)",
"rgb(201, 219, 116)",
"rgb(139, 224, 164)",
"rgb(180, 151, 231)",
"rgb(179, 179, 179)",
]
Prism = [
"rgb(95, 70, 144)",
"rgb(29, 105, 150)",
"rgb(56, 166, 165)",
"rgb(15, 133, 84)",
"rgb(115, 175, 72)",
"rgb(237, 173, 8)",
"rgb(225, 124, 5)",
"rgb(204, 80, 62)",
"rgb(148, 52, 110)",
"rgb(111, 64, 112)",
"rgb(102, 102, 102)",
]
Safe = [
"rgb(136, 204, 238)",
"rgb(204, 102, 119)",
"rgb(221, 204, 119)",
"rgb(17, 119, 51)",
"rgb(51, 34, 136)",
"rgb(170, 68, 153)",
"rgb(68, 170, 153)",
"rgb(153, 153, 51)",
"rgb(136, 34, 85)",
"rgb(102, 17, 0)",
"rgb(136, 136, 136)",
]
Vivid = [
"rgb(229, 134, 6)",
"rgb(93, 105, 177)",
"rgb(82, 188, 163)",
"rgb(153, 201, 69)",
"rgb(204, 97, 176)",
"rgb(36, 121, 108)",
"rgb(218, 165, 27)",
"rgb(47, 138, 196)",
"rgb(118, 78, 159)",
"rgb(237, 100, 90)",
"rgb(165, 170, 153)",
]
Aggrnyl_r = Aggrnyl[::-1]
Agsunset_r = Agsunset[::-1]
Antique_r = Antique[::-1]
Armyrose_r = Armyrose[::-1]
Blugrn_r = Blugrn[::-1]
Bluyl_r = Bluyl[::-1]
Bold_r = Bold[::-1]
Brwnyl_r = Brwnyl[::-1]
Burg_r = Burg[::-1]
Burgyl_r = Burgyl[::-1]
Darkmint_r = Darkmint[::-1]
Earth_r = Earth[::-1]
Emrld_r = Emrld[::-1]
Fall_r = Fall[::-1]
Geyser_r = Geyser[::-1]
Magenta_r = Magenta[::-1]
Mint_r = Mint[::-1]
Oryel_r = Oryel[::-1]
Pastel_r = Pastel[::-1]
Peach_r = Peach[::-1]
Pinkyl_r = Pinkyl[::-1]
Prism_r = Prism[::-1]
Purp_r = Purp[::-1]
Purpor_r = Purpor[::-1]
Redor_r = Redor[::-1]
Safe_r = Safe[::-1]
Sunset_r = Sunset[::-1]
Sunsetdark_r = Sunsetdark[::-1]
Teal_r = Teal[::-1]
Tealgrn_r = Tealgrn[::-1]
Tealrose_r = Tealrose[::-1]
Temps_r = Temps[::-1]
Tropic_r = Tropic[::-1]
Vivid_r = Vivid[::-1]

View File

@ -0,0 +1,296 @@
"""
Color scales from the cmocean project
Learn more at https://matplotlib.org/cmocean/
cmocean is made available under an MIT license: https://github.com/matplotlib/cmocean/blob/master/LICENSE.txt
"""
from ._swatches import _swatches, _swatches_continuous
def swatches(template=None):
return _swatches(__name__, globals(), template)
swatches.__doc__ = _swatches.__doc__
def swatches_continuous(template=None):
return _swatches_continuous(__name__, globals(), template)
swatches_continuous.__doc__ = _swatches_continuous.__doc__
turbid = [
"rgb(232, 245, 171)",
"rgb(220, 219, 137)",
"rgb(209, 193, 107)",
"rgb(199, 168, 83)",
"rgb(186, 143, 66)",
"rgb(170, 121, 60)",
"rgb(151, 103, 58)",
"rgb(129, 87, 56)",
"rgb(104, 72, 53)",
"rgb(80, 59, 46)",
"rgb(57, 45, 37)",
"rgb(34, 30, 27)",
]
thermal = [
"rgb(3, 35, 51)",
"rgb(13, 48, 100)",
"rgb(53, 50, 155)",
"rgb(93, 62, 153)",
"rgb(126, 77, 143)",
"rgb(158, 89, 135)",
"rgb(193, 100, 121)",
"rgb(225, 113, 97)",
"rgb(246, 139, 69)",
"rgb(251, 173, 60)",
"rgb(246, 211, 70)",
"rgb(231, 250, 90)",
]
haline = [
"rgb(41, 24, 107)",
"rgb(42, 35, 160)",
"rgb(15, 71, 153)",
"rgb(18, 95, 142)",
"rgb(38, 116, 137)",
"rgb(53, 136, 136)",
"rgb(65, 157, 133)",
"rgb(81, 178, 124)",
"rgb(111, 198, 107)",
"rgb(160, 214, 91)",
"rgb(212, 225, 112)",
"rgb(253, 238, 153)",
]
solar = [
"rgb(51, 19, 23)",
"rgb(79, 28, 33)",
"rgb(108, 36, 36)",
"rgb(135, 47, 32)",
"rgb(157, 66, 25)",
"rgb(174, 88, 20)",
"rgb(188, 111, 19)",
"rgb(199, 137, 22)",
"rgb(209, 164, 32)",
"rgb(217, 192, 44)",
"rgb(222, 222, 59)",
"rgb(224, 253, 74)",
]
ice = [
"rgb(3, 5, 18)",
"rgb(25, 25, 51)",
"rgb(44, 42, 87)",
"rgb(58, 60, 125)",
"rgb(62, 83, 160)",
"rgb(62, 109, 178)",
"rgb(72, 134, 187)",
"rgb(89, 159, 196)",
"rgb(114, 184, 205)",
"rgb(149, 207, 216)",
"rgb(192, 229, 232)",
"rgb(234, 252, 253)",
]
gray = [
"rgb(0, 0, 0)",
"rgb(16, 16, 16)",
"rgb(38, 38, 38)",
"rgb(59, 59, 59)",
"rgb(81, 80, 80)",
"rgb(102, 101, 101)",
"rgb(124, 123, 122)",
"rgb(146, 146, 145)",
"rgb(171, 171, 170)",
"rgb(197, 197, 195)",
"rgb(224, 224, 223)",
"rgb(254, 254, 253)",
]
oxy = [
"rgb(63, 5, 5)",
"rgb(101, 6, 13)",
"rgb(138, 17, 9)",
"rgb(96, 95, 95)",
"rgb(119, 118, 118)",
"rgb(142, 141, 141)",
"rgb(166, 166, 165)",
"rgb(193, 192, 191)",
"rgb(222, 222, 220)",
"rgb(239, 248, 90)",
"rgb(230, 210, 41)",
"rgb(220, 174, 25)",
]
deep = [
"rgb(253, 253, 204)",
"rgb(206, 236, 179)",
"rgb(156, 219, 165)",
"rgb(111, 201, 163)",
"rgb(86, 177, 163)",
"rgb(76, 153, 160)",
"rgb(68, 130, 155)",
"rgb(62, 108, 150)",
"rgb(62, 82, 143)",
"rgb(64, 60, 115)",
"rgb(54, 43, 77)",
"rgb(39, 26, 44)",
]
dense = [
"rgb(230, 240, 240)",
"rgb(191, 221, 229)",
"rgb(156, 201, 226)",
"rgb(129, 180, 227)",
"rgb(115, 154, 228)",
"rgb(117, 127, 221)",
"rgb(120, 100, 202)",
"rgb(119, 74, 175)",
"rgb(113, 50, 141)",
"rgb(100, 31, 104)",
"rgb(80, 20, 66)",
"rgb(54, 14, 36)",
]
algae = [
"rgb(214, 249, 207)",
"rgb(186, 228, 174)",
"rgb(156, 209, 143)",
"rgb(124, 191, 115)",
"rgb(85, 174, 91)",
"rgb(37, 157, 81)",
"rgb(7, 138, 78)",
"rgb(13, 117, 71)",
"rgb(23, 95, 61)",
"rgb(25, 75, 49)",
"rgb(23, 55, 35)",
"rgb(17, 36, 20)",
]
matter = [
"rgb(253, 237, 176)",
"rgb(250, 205, 145)",
"rgb(246, 173, 119)",
"rgb(240, 142, 98)",
"rgb(231, 109, 84)",
"rgb(216, 80, 83)",
"rgb(195, 56, 90)",
"rgb(168, 40, 96)",
"rgb(138, 29, 99)",
"rgb(107, 24, 93)",
"rgb(76, 21, 80)",
"rgb(47, 15, 61)",
]
speed = [
"rgb(254, 252, 205)",
"rgb(239, 225, 156)",
"rgb(221, 201, 106)",
"rgb(194, 182, 59)",
"rgb(157, 167, 21)",
"rgb(116, 153, 5)",
"rgb(75, 138, 20)",
"rgb(35, 121, 36)",
"rgb(11, 100, 44)",
"rgb(18, 78, 43)",
"rgb(25, 56, 34)",
"rgb(23, 35, 18)",
]
amp = [
"rgb(241, 236, 236)",
"rgb(230, 209, 203)",
"rgb(221, 182, 170)",
"rgb(213, 156, 137)",
"rgb(205, 129, 103)",
"rgb(196, 102, 73)",
"rgb(186, 74, 47)",
"rgb(172, 44, 36)",
"rgb(149, 19, 39)",
"rgb(120, 14, 40)",
"rgb(89, 13, 31)",
"rgb(60, 9, 17)",
]
tempo = [
"rgb(254, 245, 244)",
"rgb(222, 224, 210)",
"rgb(189, 206, 181)",
"rgb(153, 189, 156)",
"rgb(110, 173, 138)",
"rgb(65, 157, 129)",
"rgb(25, 137, 125)",
"rgb(18, 116, 117)",
"rgb(25, 94, 106)",
"rgb(28, 72, 93)",
"rgb(25, 51, 80)",
"rgb(20, 29, 67)",
]
phase = [
"rgb(167, 119, 12)",
"rgb(197, 96, 51)",
"rgb(217, 67, 96)",
"rgb(221, 38, 163)",
"rgb(196, 59, 224)",
"rgb(153, 97, 244)",
"rgb(95, 127, 228)",
"rgb(40, 144, 183)",
"rgb(15, 151, 136)",
"rgb(39, 153, 79)",
"rgb(119, 141, 17)",
"rgb(167, 119, 12)",
]
balance = [
"rgb(23, 28, 66)",
"rgb(41, 58, 143)",
"rgb(11, 102, 189)",
"rgb(69, 144, 185)",
"rgb(142, 181, 194)",
"rgb(210, 216, 219)",
"rgb(230, 210, 204)",
"rgb(213, 157, 137)",
"rgb(196, 101, 72)",
"rgb(172, 43, 36)",
"rgb(120, 14, 40)",
"rgb(60, 9, 17)",
]
delta = [
"rgb(16, 31, 63)",
"rgb(38, 62, 144)",
"rgb(30, 110, 161)",
"rgb(60, 154, 171)",
"rgb(140, 193, 186)",
"rgb(217, 229, 218)",
"rgb(239, 226, 156)",
"rgb(195, 182, 59)",
"rgb(115, 152, 5)",
"rgb(34, 120, 36)",
"rgb(18, 78, 43)",
"rgb(23, 35, 18)",
]
curl = [
"rgb(20, 29, 67)",
"rgb(28, 72, 93)",
"rgb(18, 115, 117)",
"rgb(63, 156, 129)",
"rgb(153, 189, 156)",
"rgb(223, 225, 211)",
"rgb(241, 218, 206)",
"rgb(224, 160, 137)",
"rgb(203, 101, 99)",
"rgb(164, 54, 96)",
"rgb(111, 23, 91)",
"rgb(51, 13, 53)",
]
algae_r = algae[::-1]
amp_r = amp[::-1]
balance_r = balance[::-1]
curl_r = curl[::-1]
deep_r = deep[::-1]
delta_r = delta[::-1]
dense_r = dense[::-1]
gray_r = gray[::-1]
haline_r = haline[::-1]
ice_r = ice[::-1]
matter_r = matter[::-1]
oxy_r = oxy[::-1]
phase_r = phase[::-1]
solar_r = solar[::-1]
speed_r = speed[::-1]
tempo_r = tempo[::-1]
thermal_r = thermal[::-1]
turbid_r = turbid[::-1]

View File

@ -0,0 +1,494 @@
"""
Color scales and sequences from the colorbrewer 2 project
Learn more at http://colorbrewer2.org
colorbrewer is made available under an Apache license: http://colorbrewer2.org/export/LICENSE.txt
"""
from ._swatches import _swatches
def swatches(template=None):
return _swatches(__name__, globals(), template)
swatches.__doc__ = _swatches.__doc__
BrBG = [
"rgb(84,48,5)",
"rgb(140,81,10)",
"rgb(191,129,45)",
"rgb(223,194,125)",
"rgb(246,232,195)",
"rgb(245,245,245)",
"rgb(199,234,229)",
"rgb(128,205,193)",
"rgb(53,151,143)",
"rgb(1,102,94)",
"rgb(0,60,48)",
]
PRGn = [
"rgb(64,0,75)",
"rgb(118,42,131)",
"rgb(153,112,171)",
"rgb(194,165,207)",
"rgb(231,212,232)",
"rgb(247,247,247)",
"rgb(217,240,211)",
"rgb(166,219,160)",
"rgb(90,174,97)",
"rgb(27,120,55)",
"rgb(0,68,27)",
]
PiYG = [
"rgb(142,1,82)",
"rgb(197,27,125)",
"rgb(222,119,174)",
"rgb(241,182,218)",
"rgb(253,224,239)",
"rgb(247,247,247)",
"rgb(230,245,208)",
"rgb(184,225,134)",
"rgb(127,188,65)",
"rgb(77,146,33)",
"rgb(39,100,25)",
]
PuOr = [
"rgb(127,59,8)",
"rgb(179,88,6)",
"rgb(224,130,20)",
"rgb(253,184,99)",
"rgb(254,224,182)",
"rgb(247,247,247)",
"rgb(216,218,235)",
"rgb(178,171,210)",
"rgb(128,115,172)",
"rgb(84,39,136)",
"rgb(45,0,75)",
]
RdBu = [
"rgb(103,0,31)",
"rgb(178,24,43)",
"rgb(214,96,77)",
"rgb(244,165,130)",
"rgb(253,219,199)",
"rgb(247,247,247)",
"rgb(209,229,240)",
"rgb(146,197,222)",
"rgb(67,147,195)",
"rgb(33,102,172)",
"rgb(5,48,97)",
]
RdGy = [
"rgb(103,0,31)",
"rgb(178,24,43)",
"rgb(214,96,77)",
"rgb(244,165,130)",
"rgb(253,219,199)",
"rgb(255,255,255)",
"rgb(224,224,224)",
"rgb(186,186,186)",
"rgb(135,135,135)",
"rgb(77,77,77)",
"rgb(26,26,26)",
]
RdYlBu = [
"rgb(165,0,38)",
"rgb(215,48,39)",
"rgb(244,109,67)",
"rgb(253,174,97)",
"rgb(254,224,144)",
"rgb(255,255,191)",
"rgb(224,243,248)",
"rgb(171,217,233)",
"rgb(116,173,209)",
"rgb(69,117,180)",
"rgb(49,54,149)",
]
RdYlGn = [
"rgb(165,0,38)",
"rgb(215,48,39)",
"rgb(244,109,67)",
"rgb(253,174,97)",
"rgb(254,224,139)",
"rgb(255,255,191)",
"rgb(217,239,139)",
"rgb(166,217,106)",
"rgb(102,189,99)",
"rgb(26,152,80)",
"rgb(0,104,55)",
]
Spectral = [
"rgb(158,1,66)",
"rgb(213,62,79)",
"rgb(244,109,67)",
"rgb(253,174,97)",
"rgb(254,224,139)",
"rgb(255,255,191)",
"rgb(230,245,152)",
"rgb(171,221,164)",
"rgb(102,194,165)",
"rgb(50,136,189)",
"rgb(94,79,162)",
]
Set1 = [
"rgb(228,26,28)",
"rgb(55,126,184)",
"rgb(77,175,74)",
"rgb(152,78,163)",
"rgb(255,127,0)",
"rgb(255,255,51)",
"rgb(166,86,40)",
"rgb(247,129,191)",
"rgb(153,153,153)",
]
Pastel1 = [
"rgb(251,180,174)",
"rgb(179,205,227)",
"rgb(204,235,197)",
"rgb(222,203,228)",
"rgb(254,217,166)",
"rgb(255,255,204)",
"rgb(229,216,189)",
"rgb(253,218,236)",
"rgb(242,242,242)",
]
Dark2 = [
"rgb(27,158,119)",
"rgb(217,95,2)",
"rgb(117,112,179)",
"rgb(231,41,138)",
"rgb(102,166,30)",
"rgb(230,171,2)",
"rgb(166,118,29)",
"rgb(102,102,102)",
]
Set2 = [
"rgb(102,194,165)",
"rgb(252,141,98)",
"rgb(141,160,203)",
"rgb(231,138,195)",
"rgb(166,216,84)",
"rgb(255,217,47)",
"rgb(229,196,148)",
"rgb(179,179,179)",
]
Pastel2 = [
"rgb(179,226,205)",
"rgb(253,205,172)",
"rgb(203,213,232)",
"rgb(244,202,228)",
"rgb(230,245,201)",
"rgb(255,242,174)",
"rgb(241,226,204)",
"rgb(204,204,204)",
]
Set3 = [
"rgb(141,211,199)",
"rgb(255,255,179)",
"rgb(190,186,218)",
"rgb(251,128,114)",
"rgb(128,177,211)",
"rgb(253,180,98)",
"rgb(179,222,105)",
"rgb(252,205,229)",
"rgb(217,217,217)",
"rgb(188,128,189)",
"rgb(204,235,197)",
"rgb(255,237,111)",
]
Accent = [
"rgb(127,201,127)",
"rgb(190,174,212)",
"rgb(253,192,134)",
"rgb(255,255,153)",
"rgb(56,108,176)",
"rgb(240,2,127)",
"rgb(191,91,23)",
"rgb(102,102,102)",
]
Paired = [
"rgb(166,206,227)",
"rgb(31,120,180)",
"rgb(178,223,138)",
"rgb(51,160,44)",
"rgb(251,154,153)",
"rgb(227,26,28)",
"rgb(253,191,111)",
"rgb(255,127,0)",
"rgb(202,178,214)",
"rgb(106,61,154)",
"rgb(255,255,153)",
"rgb(177,89,40)",
]
Blues = [
"rgb(247,251,255)",
"rgb(222,235,247)",
"rgb(198,219,239)",
"rgb(158,202,225)",
"rgb(107,174,214)",
"rgb(66,146,198)",
"rgb(33,113,181)",
"rgb(8,81,156)",
"rgb(8,48,107)",
]
BuGn = [
"rgb(247,252,253)",
"rgb(229,245,249)",
"rgb(204,236,230)",
"rgb(153,216,201)",
"rgb(102,194,164)",
"rgb(65,174,118)",
"rgb(35,139,69)",
"rgb(0,109,44)",
"rgb(0,68,27)",
]
BuPu = [
"rgb(247,252,253)",
"rgb(224,236,244)",
"rgb(191,211,230)",
"rgb(158,188,218)",
"rgb(140,150,198)",
"rgb(140,107,177)",
"rgb(136,65,157)",
"rgb(129,15,124)",
"rgb(77,0,75)",
]
GnBu = [
"rgb(247,252,240)",
"rgb(224,243,219)",
"rgb(204,235,197)",
"rgb(168,221,181)",
"rgb(123,204,196)",
"rgb(78,179,211)",
"rgb(43,140,190)",
"rgb(8,104,172)",
"rgb(8,64,129)",
]
Greens = [
"rgb(247,252,245)",
"rgb(229,245,224)",
"rgb(199,233,192)",
"rgb(161,217,155)",
"rgb(116,196,118)",
"rgb(65,171,93)",
"rgb(35,139,69)",
"rgb(0,109,44)",
"rgb(0,68,27)",
]
Greys = [
"rgb(255,255,255)",
"rgb(240,240,240)",
"rgb(217,217,217)",
"rgb(189,189,189)",
"rgb(150,150,150)",
"rgb(115,115,115)",
"rgb(82,82,82)",
"rgb(37,37,37)",
"rgb(0,0,0)",
]
OrRd = [
"rgb(255,247,236)",
"rgb(254,232,200)",
"rgb(253,212,158)",
"rgb(253,187,132)",
"rgb(252,141,89)",
"rgb(239,101,72)",
"rgb(215,48,31)",
"rgb(179,0,0)",
"rgb(127,0,0)",
]
Oranges = [
"rgb(255,245,235)",
"rgb(254,230,206)",
"rgb(253,208,162)",
"rgb(253,174,107)",
"rgb(253,141,60)",
"rgb(241,105,19)",
"rgb(217,72,1)",
"rgb(166,54,3)",
"rgb(127,39,4)",
]
PuBu = [
"rgb(255,247,251)",
"rgb(236,231,242)",
"rgb(208,209,230)",
"rgb(166,189,219)",
"rgb(116,169,207)",
"rgb(54,144,192)",
"rgb(5,112,176)",
"rgb(4,90,141)",
"rgb(2,56,88)",
]
PuBuGn = [
"rgb(255,247,251)",
"rgb(236,226,240)",
"rgb(208,209,230)",
"rgb(166,189,219)",
"rgb(103,169,207)",
"rgb(54,144,192)",
"rgb(2,129,138)",
"rgb(1,108,89)",
"rgb(1,70,54)",
]
PuRd = [
"rgb(247,244,249)",
"rgb(231,225,239)",
"rgb(212,185,218)",
"rgb(201,148,199)",
"rgb(223,101,176)",
"rgb(231,41,138)",
"rgb(206,18,86)",
"rgb(152,0,67)",
"rgb(103,0,31)",
]
Purples = [
"rgb(252,251,253)",
"rgb(239,237,245)",
"rgb(218,218,235)",
"rgb(188,189,220)",
"rgb(158,154,200)",
"rgb(128,125,186)",
"rgb(106,81,163)",
"rgb(84,39,143)",
"rgb(63,0,125)",
]
RdPu = [
"rgb(255,247,243)",
"rgb(253,224,221)",
"rgb(252,197,192)",
"rgb(250,159,181)",
"rgb(247,104,161)",
"rgb(221,52,151)",
"rgb(174,1,126)",
"rgb(122,1,119)",
"rgb(73,0,106)",
]
Reds = [
"rgb(255,245,240)",
"rgb(254,224,210)",
"rgb(252,187,161)",
"rgb(252,146,114)",
"rgb(251,106,74)",
"rgb(239,59,44)",
"rgb(203,24,29)",
"rgb(165,15,21)",
"rgb(103,0,13)",
]
YlGn = [
"rgb(255,255,229)",
"rgb(247,252,185)",
"rgb(217,240,163)",
"rgb(173,221,142)",
"rgb(120,198,121)",
"rgb(65,171,93)",
"rgb(35,132,67)",
"rgb(0,104,55)",
"rgb(0,69,41)",
]
YlGnBu = [
"rgb(255,255,217)",
"rgb(237,248,177)",
"rgb(199,233,180)",
"rgb(127,205,187)",
"rgb(65,182,196)",
"rgb(29,145,192)",
"rgb(34,94,168)",
"rgb(37,52,148)",
"rgb(8,29,88)",
]
YlOrBr = [
"rgb(255,255,229)",
"rgb(255,247,188)",
"rgb(254,227,145)",
"rgb(254,196,79)",
"rgb(254,153,41)",
"rgb(236,112,20)",
"rgb(204,76,2)",
"rgb(153,52,4)",
"rgb(102,37,6)",
]
YlOrRd = [
"rgb(255,255,204)",
"rgb(255,237,160)",
"rgb(254,217,118)",
"rgb(254,178,76)",
"rgb(253,141,60)",
"rgb(252,78,42)",
"rgb(227,26,28)",
"rgb(189,0,38)",
"rgb(128,0,38)",
]
Accent_r = Accent[::-1]
Blues_r = Blues[::-1]
BrBG_r = BrBG[::-1]
BuGn_r = BuGn[::-1]
BuPu_r = BuPu[::-1]
Dark2_r = Dark2[::-1]
GnBu_r = GnBu[::-1]
Greens_r = Greens[::-1]
Greys_r = Greys[::-1]
OrRd_r = OrRd[::-1]
Oranges_r = Oranges[::-1]
PRGn_r = PRGn[::-1]
Paired_r = Paired[::-1]
Pastel1_r = Pastel1[::-1]
Pastel2_r = Pastel2[::-1]
PiYG_r = PiYG[::-1]
PuBu_r = PuBu[::-1]
PuBuGn_r = PuBuGn[::-1]
PuOr_r = PuOr[::-1]
PuRd_r = PuRd[::-1]
Purples_r = Purples[::-1]
RdBu_r = RdBu[::-1]
RdGy_r = RdGy[::-1]
RdPu_r = RdPu[::-1]
RdYlBu_r = RdYlBu[::-1]
RdYlGn_r = RdYlGn[::-1]
Reds_r = Reds[::-1]
Set1_r = Set1[::-1]
Set2_r = Set2[::-1]
Set3_r = Set3[::-1]
Spectral_r = Spectral[::-1]
YlGn_r = YlGn[::-1]
YlGnBu_r = YlGnBu[::-1]
YlOrBr_r = YlOrBr[::-1]
YlOrRd_r = YlOrRd[::-1]

View File

@ -0,0 +1,157 @@
"""
Cyclical color scales are appropriate for continuous data that has a natural cyclical \
structure, such as temporal data (hour of day, day of week, day of year, seasons) or
complex numbers or other phase data.
"""
from ._swatches import _swatches, _swatches_continuous, _swatches_cyclical
def swatches(template=None):
return _swatches(__name__, globals(), template)
swatches.__doc__ = _swatches.__doc__
def swatches_continuous(template=None):
return _swatches_continuous(__name__, globals(), template)
swatches_continuous.__doc__ = _swatches_continuous.__doc__
def swatches_cyclical(template=None):
return _swatches_cyclical(__name__, globals(), template)
swatches_cyclical.__doc__ = _swatches_cyclical.__doc__
Twilight = [
"#e2d9e2",
"#9ebbc9",
"#6785be",
"#5e43a5",
"#421257",
"#471340",
"#8e2c50",
"#ba6657",
"#ceac94",
"#e2d9e2",
]
IceFire = [
"#000000",
"#001f4d",
"#003786",
"#0e58a8",
"#217eb8",
"#30a4ca",
"#54c8df",
"#9be4ef",
"#e1e9d1",
"#f3d573",
"#e7b000",
"#da8200",
"#c65400",
"#ac2301",
"#820000",
"#4c0000",
"#000000",
]
Edge = [
"#313131",
"#3d019d",
"#3810dc",
"#2d47f9",
"#2593ff",
"#2adef6",
"#60fdfa",
"#aefdff",
"#f3f3f1",
"#fffda9",
"#fafd5b",
"#f7da29",
"#ff8e25",
"#f8432d",
"#d90d39",
"#97023d",
"#313131",
]
Phase = [
"rgb(167, 119, 12)",
"rgb(197, 96, 51)",
"rgb(217, 67, 96)",
"rgb(221, 38, 163)",
"rgb(196, 59, 224)",
"rgb(153, 97, 244)",
"rgb(95, 127, 228)",
"rgb(40, 144, 183)",
"rgb(15, 151, 136)",
"rgb(39, 153, 79)",
"rgb(119, 141, 17)",
"rgb(167, 119, 12)",
]
HSV = [
"#ff0000",
"#ffa700",
"#afff00",
"#08ff00",
"#00ff9f",
"#00b7ff",
"#0010ff",
"#9700ff",
"#ff00bf",
"#ff0000",
]
mrybm = [
"#f884f7",
"#f968c4",
"#ea4388",
"#cf244b",
"#b51a15",
"#bd4304",
"#cc6904",
"#d58f04",
"#cfaa27",
"#a19f62",
"#588a93",
"#2269c4",
"#3e3ef0",
"#6b4ef9",
"#956bfa",
"#cd7dfe",
"#f884f7",
]
mygbm = [
"#ef55f1",
"#fb84ce",
"#fbafa1",
"#fcd471",
"#f0ed35",
"#c6e516",
"#96d310",
"#61c10b",
"#31ac28",
"#439064",
"#3d719a",
"#284ec8",
"#2e21ea",
"#6324f5",
"#9139fa",
"#c543fa",
"#ef55f1",
]
Edge_r = Edge[::-1]
HSV_r = HSV[::-1]
IceFire_r = IceFire[::-1]
Phase_r = Phase[::-1]
Twilight_r = Twilight[::-1]
mrybm_r = mrybm[::-1]
mygbm_r = mygbm[::-1]
__all__ = [
"swatches",
"swatches_cyclical",
]

View File

@ -0,0 +1,75 @@
"""
Diverging color scales are appropriate for continuous data that has a natural midpoint \
other otherwise informative special value, such as 0 altitude, or the boiling point
of a liquid. The color scales in this module are \
mostly meant to be passed in as the `color_continuous_scale` argument to various \
functions, and to be used with the `color_continuous_midpoint` argument.
"""
from .colorbrewer import ( # noqa: F401
BrBG,
PRGn,
PiYG,
PuOr,
RdBu,
RdGy,
RdYlBu,
RdYlGn,
Spectral,
BrBG_r,
PRGn_r,
PiYG_r,
PuOr_r,
RdBu_r,
RdGy_r,
RdYlBu_r,
RdYlGn_r,
Spectral_r,
)
from .cmocean import ( # noqa: F401
balance,
delta,
curl,
oxy,
balance_r,
delta_r,
curl_r,
oxy_r,
)
from .carto import ( # noqa: F401
Armyrose,
Fall,
Geyser,
Temps,
Tealrose,
Tropic,
Earth,
Armyrose_r,
Fall_r,
Geyser_r,
Temps_r,
Tealrose_r,
Tropic_r,
Earth_r,
)
from .plotlyjs import Picnic, Portland, Picnic_r, Portland_r # noqa: F401
from ._swatches import _swatches, _swatches_continuous
def swatches(template=None):
return _swatches(__name__, globals(), template)
swatches.__doc__ = _swatches.__doc__
def swatches_continuous(template=None):
return _swatches_continuous(__name__, globals(), template)
swatches_continuous.__doc__ = _swatches_continuous.__doc__
__all__ = ["swatches"]

View File

@ -0,0 +1,180 @@
# Copied from
# https://github.com/plotly/plotly.js/blob/master/src/components/colorscale/scales.js
# NOTE: these differ slightly from plotly.colors.PLOTLY_SCALES from Plotly.js because
# those ones don't have perfectly evenly spaced steps ...
# not sure when this skew was introduced, possibly as early as Plotly.py v4.0
Blackbody = [
"rgb(0,0,0)",
"rgb(230,0,0)",
"rgb(230,210,0)",
"rgb(255,255,255)",
"rgb(160,200,255)",
]
Bluered = ["rgb(0,0,255)", "rgb(255,0,0)"]
Blues = [
"rgb(5,10,172)",
"rgb(40,60,190)",
"rgb(70,100,245)",
"rgb(90,120,245)",
"rgb(106,137,247)",
"rgb(220,220,220)",
]
Cividis = [
"rgb(0,32,76)",
"rgb(0,42,102)",
"rgb(0,52,110)",
"rgb(39,63,108)",
"rgb(60,74,107)",
"rgb(76,85,107)",
"rgb(91,95,109)",
"rgb(104,106,112)",
"rgb(117,117,117)",
"rgb(131,129,120)",
"rgb(146,140,120)",
"rgb(161,152,118)",
"rgb(176,165,114)",
"rgb(192,177,109)",
"rgb(209,191,102)",
"rgb(225,204,92)",
"rgb(243,219,79)",
"rgb(255,233,69)",
]
Earth = [
"rgb(0,0,130)",
"rgb(0,180,180)",
"rgb(40,210,40)",
"rgb(230,230,50)",
"rgb(120,70,20)",
"rgb(255,255,255)",
]
Electric = [
"rgb(0,0,0)",
"rgb(30,0,100)",
"rgb(120,0,100)",
"rgb(160,90,0)",
"rgb(230,200,0)",
"rgb(255,250,220)",
]
Greens = [
"rgb(0,68,27)",
"rgb(0,109,44)",
"rgb(35,139,69)",
"rgb(65,171,93)",
"rgb(116,196,118)",
"rgb(161,217,155)",
"rgb(199,233,192)",
"rgb(229,245,224)",
"rgb(247,252,245)",
]
Greys = ["rgb(0,0,0)", "rgb(255,255,255)"]
Hot = ["rgb(0,0,0)", "rgb(230,0,0)", "rgb(255,210,0)", "rgb(255,255,255)"]
Jet = [
"rgb(0,0,131)",
"rgb(0,60,170)",
"rgb(5,255,255)",
"rgb(255,255,0)",
"rgb(250,0,0)",
"rgb(128,0,0)",
]
Picnic = [
"rgb(0,0,255)",
"rgb(51,153,255)",
"rgb(102,204,255)",
"rgb(153,204,255)",
"rgb(204,204,255)",
"rgb(255,255,255)",
"rgb(255,204,255)",
"rgb(255,153,255)",
"rgb(255,102,204)",
"rgb(255,102,102)",
"rgb(255,0,0)",
]
Portland = [
"rgb(12,51,131)",
"rgb(10,136,186)",
"rgb(242,211,56)",
"rgb(242,143,56)",
"rgb(217,30,30)",
]
Rainbow = [
"rgb(150,0,90)",
"rgb(0,0,200)",
"rgb(0,25,255)",
"rgb(0,152,255)",
"rgb(44,255,150)",
"rgb(151,255,0)",
"rgb(255,234,0)",
"rgb(255,111,0)",
"rgb(255,0,0)",
]
RdBu = [
"rgb(5,10,172)",
"rgb(106,137,247)",
"rgb(190,190,190)",
"rgb(220,170,132)",
"rgb(230,145,90)",
"rgb(178,10,28)",
]
Reds = ["rgb(220,220,220)", "rgb(245,195,157)", "rgb(245,160,105)", "rgb(178,10,28)"]
Viridis = [
"#440154",
"#48186a",
"#472d7b",
"#424086",
"#3b528b",
"#33638d",
"#2c728e",
"#26828e",
"#21918c",
"#1fa088",
"#28ae80",
"#3fbc73",
"#5ec962",
"#84d44b",
"#addc30",
"#d8e219",
"#fde725",
]
YlGnBu = [
"rgb(8,29,88)",
"rgb(37,52,148)",
"rgb(34,94,168)",
"rgb(29,145,192)",
"rgb(65,182,196)",
"rgb(127,205,187)",
"rgb(199,233,180)",
"rgb(237,248,217)",
"rgb(255,255,217)",
]
YlOrRd = [
"rgb(128,0,38)",
"rgb(189,0,38)",
"rgb(227,26,28)",
"rgb(252,78,42)",
"rgb(253,141,60)",
"rgb(254,178,76)",
"rgb(254,217,118)",
"rgb(255,237,160)",
"rgb(255,255,204)",
]
Blackbody_r = Blackbody[::-1]
Bluered_r = Bluered[::-1]
Blues_r = Blues[::-1]
Cividis_r = Cividis[::-1]
Earth_r = Earth[::-1]
Electric_r = Electric[::-1]
Greens_r = Greens[::-1]
Greys_r = Greys[::-1]
Hot_r = Hot[::-1]
Jet_r = Jet[::-1]
Picnic_r = Picnic[::-1]
Portland_r = Portland[::-1]
Rainbow_r = Rainbow[::-1]
RdBu_r = RdBu[::-1]
Reds_r = Reds[::-1]
Viridis_r = Viridis[::-1]
YlGnBu_r = YlGnBu[::-1]
YlOrRd_r = YlOrRd[::-1]

View File

@ -0,0 +1,184 @@
"""
Qualitative color sequences are appropriate for data that has no natural ordering, such \
as categories, colors, names, countries etc. The color sequences in this module are \
mostly meant to be passed in as the `color_discrete_sequence` argument to various functions.
"""
from ._swatches import _swatches
def swatches(template=None):
return _swatches(__name__, globals(), template)
swatches.__doc__ = _swatches.__doc__
Plotly = [
"#636EFA",
"#EF553B",
"#00CC96",
"#AB63FA",
"#FFA15A",
"#19D3F3",
"#FF6692",
"#B6E880",
"#FF97FF",
"#FECB52",
]
D3 = [
"#1F77B4",
"#FF7F0E",
"#2CA02C",
"#D62728",
"#9467BD",
"#8C564B",
"#E377C2",
"#7F7F7F",
"#BCBD22",
"#17BECF",
]
G10 = [
"#3366CC",
"#DC3912",
"#FF9900",
"#109618",
"#990099",
"#0099C6",
"#DD4477",
"#66AA00",
"#B82E2E",
"#316395",
]
T10 = [
"#4C78A8",
"#F58518",
"#E45756",
"#72B7B2",
"#54A24B",
"#EECA3B",
"#B279A2",
"#FF9DA6",
"#9D755D",
"#BAB0AC",
]
Alphabet = [
"#AA0DFE",
"#3283FE",
"#85660D",
"#782AB6",
"#565656",
"#1C8356",
"#16FF32",
"#F7E1A0",
"#E2E2E2",
"#1CBE4F",
"#C4451C",
"#DEA0FD",
"#FE00FA",
"#325A9B",
"#FEAF16",
"#F8A19F",
"#90AD1C",
"#F6222E",
"#1CFFCE",
"#2ED9FF",
"#B10DA1",
"#C075A6",
"#FC1CBF",
"#B00068",
"#FBE426",
"#FA0087",
]
Dark24 = [
"#2E91E5",
"#E15F99",
"#1CA71C",
"#FB0D0D",
"#DA16FF",
"#222A2A",
"#B68100",
"#750D86",
"#EB663B",
"#511CFB",
"#00A08B",
"#FB00D1",
"#FC0080",
"#B2828D",
"#6C7C32",
"#778AAE",
"#862A16",
"#A777F1",
"#620042",
"#1616A7",
"#DA60CA",
"#6C4516",
"#0D2A63",
"#AF0038",
]
Light24 = [
"#FD3216",
"#00FE35",
"#6A76FC",
"#FED4C4",
"#FE00CE",
"#0DF9FF",
"#F6F926",
"#FF9616",
"#479B55",
"#EEA6FB",
"#DC587D",
"#D626FF",
"#6E899C",
"#00B5F7",
"#B68E00",
"#C9FBE5",
"#FF0092",
"#22FFA7",
"#E3EE9E",
"#86CE00",
"#BC7196",
"#7E7DCD",
"#FC6955",
"#E48F72",
]
Alphabet_r = Alphabet[::-1]
D3_r = D3[::-1]
Dark24_r = Dark24[::-1]
G10_r = G10[::-1]
Light24_r = Light24[::-1]
Plotly_r = Plotly[::-1]
T10_r = T10[::-1]
from .colorbrewer import ( # noqa: E402 F401
Set1,
Pastel1,
Dark2,
Set2,
Pastel2,
Set3,
Set1_r,
Pastel1_r,
Dark2_r,
Set2_r,
Pastel2_r,
Set3_r,
)
from .carto import ( # noqa: E402 F401
Antique,
Bold,
Pastel,
Prism,
Safe,
Vivid,
Antique_r,
Bold_r,
Pastel_r,
Prism_r,
Safe_r,
Vivid_r,
)
__all__ = ["swatches"]

View File

@ -0,0 +1,257 @@
"""
Sequential color scales are appropriate for most continuous data, but in some cases it \
can be helpful to use a `plotly.colors.diverging` or \
`plotly.colors.cyclical` scale instead. The color scales in this module are \
mostly meant to be passed in as the `color_continuous_scale` argument to various functions.
"""
from ._swatches import _swatches, _swatches_continuous
def swatches(template=None):
return _swatches(__name__, globals(), template)
swatches.__doc__ = _swatches.__doc__
def swatches_continuous(template=None):
return _swatches_continuous(__name__, globals(), template)
swatches_continuous.__doc__ = _swatches_continuous.__doc__
Plotly3 = [
"#0508b8",
"#1910d8",
"#3c19f0",
"#6b1cfb",
"#981cfd",
"#bf1cfd",
"#dd2bfd",
"#f246fe",
"#fc67fd",
"#fe88fc",
"#fea5fd",
"#febefe",
"#fec3fe",
]
Viridis = [
"#440154",
"#482878",
"#3e4989",
"#31688e",
"#26828e",
"#1f9e89",
"#35b779",
"#6ece58",
"#b5de2b",
"#fde725",
]
Cividis = [
"#00224e",
"#123570",
"#3b496c",
"#575d6d",
"#707173",
"#8a8678",
"#a59c74",
"#c3b369",
"#e1cc55",
"#fee838",
]
Inferno = [
"#000004",
"#1b0c41",
"#4a0c6b",
"#781c6d",
"#a52c60",
"#cf4446",
"#ed6925",
"#fb9b06",
"#f7d13d",
"#fcffa4",
]
Magma = [
"#000004",
"#180f3d",
"#440f76",
"#721f81",
"#9e2f7f",
"#cd4071",
"#f1605d",
"#fd9668",
"#feca8d",
"#fcfdbf",
]
Plasma = [
"#0d0887",
"#46039f",
"#7201a8",
"#9c179e",
"#bd3786",
"#d8576b",
"#ed7953",
"#fb9f3a",
"#fdca26",
"#f0f921",
]
Turbo = [
"#30123b",
"#4145ab",
"#4675ed",
"#39a2fc",
"#1bcfd4",
"#24eca6",
"#61fc6c",
"#a4fc3b",
"#d1e834",
"#f3c63a",
"#fe9b2d",
"#f36315",
"#d93806",
"#b11901",
"#7a0402",
]
Cividis_r = Cividis[::-1]
Inferno_r = Inferno[::-1]
Magma_r = Magma[::-1]
Plasma_r = Plasma[::-1]
Plotly3_r = Plotly3[::-1]
Turbo_r = Turbo[::-1]
Viridis_r = Viridis[::-1]
from .plotlyjs import ( # noqa: E402 F401
Blackbody,
Bluered,
Electric,
Hot,
Jet,
Rainbow,
Blackbody_r,
Bluered_r,
Electric_r,
Hot_r,
Jet_r,
Rainbow_r,
)
from .colorbrewer import ( # noqa: E402 F401
Blues,
BuGn,
BuPu,
GnBu,
Greens,
Greys,
OrRd,
Oranges,
PuBu,
PuBuGn,
PuRd,
Purples,
RdBu,
RdPu,
Reds,
YlGn,
YlGnBu,
YlOrBr,
YlOrRd,
Blues_r,
BuGn_r,
BuPu_r,
GnBu_r,
Greens_r,
Greys_r,
OrRd_r,
Oranges_r,
PuBu_r,
PuBuGn_r,
PuRd_r,
Purples_r,
RdBu_r,
RdPu_r,
Reds_r,
YlGn_r,
YlGnBu_r,
YlOrBr_r,
YlOrRd_r,
)
from .cmocean import ( # noqa: E402 F401
turbid,
thermal,
haline,
solar,
ice,
gray,
deep,
dense,
algae,
matter,
speed,
amp,
tempo,
turbid_r,
thermal_r,
haline_r,
solar_r,
ice_r,
gray_r,
deep_r,
dense_r,
algae_r,
matter_r,
speed_r,
amp_r,
tempo_r,
)
from .carto import ( # noqa: E402 F401
Burg,
Burgyl,
Redor,
Oryel,
Peach,
Pinkyl,
Mint,
Blugrn,
Darkmint,
Emrld,
Aggrnyl,
Bluyl,
Teal,
Tealgrn,
Purp,
Purpor,
Sunset,
Magenta,
Sunsetdark,
Agsunset,
Brwnyl,
Burg_r,
Burgyl_r,
Redor_r,
Oryel_r,
Peach_r,
Pinkyl_r,
Mint_r,
Blugrn_r,
Darkmint_r,
Emrld_r,
Aggrnyl_r,
Bluyl_r,
Teal_r,
Tealgrn_r,
Purp_r,
Purpor_r,
Sunset_r,
Magenta_r,
Sunsetdark_r,
Agsunset_r,
Brwnyl_r,
)
__all__ = ["swatches"]

View File

@ -0,0 +1,75 @@
from io import BytesIO
import base64
from .png import Writer, from_array
try:
from PIL import Image
pil_imported = True
except ImportError:
pil_imported = False
def image_array_to_data_uri(img, backend="pil", compression=4, ext="png"):
"""Converts a numpy array of uint8 into a base64 png or jpg string.
Parameters
----------
img: ndarray of uint8
array image
backend: str
'auto', 'pil' or 'pypng'. If 'auto', Pillow is used if installed,
otherwise pypng.
compression: int, between 0 and 9
compression level to be passed to the backend
ext: str, 'png' or 'jpg'
compression format used to generate b64 string
"""
# PIL and pypng error messages are quite obscure so we catch invalid compression values
if compression < 0 or compression > 9:
raise ValueError("compression level must be between 0 and 9.")
alpha = False
if img.ndim == 2:
mode = "L"
elif img.ndim == 3 and img.shape[-1] == 3:
mode = "RGB"
elif img.ndim == 3 and img.shape[-1] == 4:
mode = "RGBA"
alpha = True
else:
raise ValueError("Invalid image shape")
if backend == "auto":
backend = "pil" if pil_imported else "pypng"
if ext != "png" and backend != "pil":
raise ValueError("jpg binary strings are only available with PIL backend")
if backend == "pypng":
ndim = img.ndim
sh = img.shape
if ndim == 3:
img = img.reshape((sh[0], sh[1] * sh[2]))
w = Writer(
sh[1], sh[0], greyscale=(ndim == 2), alpha=alpha, compression=compression
)
img_png = from_array(img, mode=mode)
prefix = "data:image/png;base64,"
with BytesIO() as stream:
w.write(stream, img_png.rows)
base64_string = prefix + base64.b64encode(stream.getvalue()).decode("utf-8")
else: # pil
if not pil_imported:
raise ImportError(
"pillow needs to be installed to use `backend='pil'. Please"
"install pillow or use `backend='pypng'."
)
pil_img = Image.fromarray(img)
if ext == "jpg" or ext == "jpeg":
prefix = "data:image/jpeg;base64,"
ext = "jpeg"
else:
prefix = "data:image/png;base64,"
ext = "png"
with BytesIO() as stream:
pil_img.save(stream, format=ext, compress_level=compression)
base64_string = prefix + base64.b64encode(stream.getvalue()).decode("utf-8")
return base64_string

View File

@ -0,0 +1,97 @@
class PlotlyError(Exception):
pass
class PlotlyEmptyDataError(PlotlyError):
pass
class PlotlyGraphObjectError(PlotlyError):
def __init__(self, message="", path=(), notes=()):
"""
General graph object error for validation failures.
:param (str|unicode) message: The error message.
:param (iterable) path: A path pointing to the error.
:param notes: Add additional notes, but keep default exception message.
"""
self.message = message
self.plain_message = message # for backwards compat
self.path = list(path)
self.notes = notes
super(PlotlyGraphObjectError, self).__init__(message)
def __str__(self):
"""This is called by Python to present the error message."""
format_dict = {
"message": self.message,
"path": "[" + "][".join(repr(k) for k in self.path) + "]",
"notes": "\n".join(self.notes),
}
return "{message}\n\nPath To Error: {path}\n\n{notes}".format(**format_dict)
class PlotlyDictKeyError(PlotlyGraphObjectError):
def __init__(self, obj, path, notes=()):
"""See PlotlyGraphObjectError.__init__ for param docs."""
format_dict = {"attribute": path[-1], "object_name": obj._name}
message = "'{attribute}' is not allowed in '{object_name}'".format(
**format_dict
)
notes = [obj.help(return_help=True)] + list(notes)
super(PlotlyDictKeyError, self).__init__(
message=message, path=path, notes=notes
)
class PlotlyDictValueError(PlotlyGraphObjectError):
def __init__(self, obj, path, notes=()):
"""See PlotlyGraphObjectError.__init__ for param docs."""
format_dict = {"attribute": path[-1], "object_name": obj._name}
message = "'{attribute}' has invalid value inside '{object_name}'".format(
**format_dict
)
notes = [obj.help(path[-1], return_help=True)] + list(notes)
super(PlotlyDictValueError, self).__init__(
message=message, notes=notes, path=path
)
class PlotlyListEntryError(PlotlyGraphObjectError):
def __init__(self, obj, path, notes=()):
"""See PlotlyGraphObjectError.__init__ for param docs."""
format_dict = {"index": path[-1], "object_name": obj._name}
message = "Invalid entry found in '{object_name}' at index, '{index}'".format(
**format_dict
)
notes = [obj.help(return_help=True)] + list(notes)
super(PlotlyListEntryError, self).__init__(
message=message, path=path, notes=notes
)
class PlotlyDataTypeError(PlotlyGraphObjectError):
def __init__(self, obj, path, notes=()):
"""See PlotlyGraphObjectError.__init__ for param docs."""
format_dict = {"index": path[-1], "object_name": obj._name}
message = "Invalid entry found in '{object_name}' at index, '{index}'".format(
**format_dict
)
note = "It's invalid because it doesn't contain a valid 'type' value."
notes = [note] + list(notes)
super(PlotlyDataTypeError, self).__init__(
message=message, path=path, notes=notes
)
class PlotlyKeyError(KeyError):
"""
KeyErrors are not printed as beautifully as other errors (this is so that
{}[''] prints "KeyError: ''" and not "KeyError:"). So here we use
LookupError's __str__ to make a PlotlyKeyError object which will print nicer
error messages for KeyErrors.
"""
def __str__(self):
return LookupError.__str__(self)

View File

@ -0,0 +1,37 @@
import os
PLOTLY_DIR = os.environ.get(
"PLOTLY_DIR", os.path.join(os.path.expanduser("~"), ".plotly")
)
TEST_FILE = os.path.join(PLOTLY_DIR, ".permission_test")
def _permissions():
try:
if not os.path.exists(PLOTLY_DIR):
try:
os.mkdir(PLOTLY_DIR)
except Exception:
# in case of race
if not os.path.isdir(PLOTLY_DIR):
raise
with open(TEST_FILE, "w") as f:
f.write("testing\n")
try:
os.remove(TEST_FILE)
except Exception:
pass
return True
except Exception: # Do not trap KeyboardInterrupt.
return False
_file_permissions = None
def ensure_writable_plotly_dir():
# Cache permissions status
global _file_permissions
if _file_permissions is None:
_file_permissions = _permissions()
return _file_permissions

View File

@ -0,0 +1,50 @@
import importlib
def relative_import(parent_name, rel_modules=(), rel_classes=()):
"""
Helper function to import submodules lazily in Python 3.7+
Parameters
----------
rel_modules: list of str
list of submodules to import, of the form .submodule
rel_classes: list of str
list of submodule classes/variables to import, of the form ._submodule.Foo
Returns
-------
tuple
Tuple that should be assigned to __all__, __getattr__ in the caller
"""
module_names = {rel_module.split(".")[-1]: rel_module for rel_module in rel_modules}
class_names = {rel_path.split(".")[-1]: rel_path for rel_path in rel_classes}
def __getattr__(import_name):
# In Python 3.7+, lazy import submodules
# Check for submodule
if import_name in module_names:
rel_import = module_names[import_name]
return importlib.import_module(rel_import, parent_name)
# Check for submodule class
if import_name in class_names:
rel_path_parts = class_names[import_name].split(".")
rel_module = ".".join(rel_path_parts[:-1])
class_name = import_name
class_module = importlib.import_module(rel_module, parent_name)
return getattr(class_module, class_name)
raise AttributeError(
"module {__name__!r} has no attribute {name!r}".format(
name=import_name, __name__=parent_name
)
)
__all__ = list(module_names) + list(class_names)
def __dir__():
return __all__
return __all__, __getattr__, __dir__

View File

@ -0,0 +1,36 @@
"""
Stand-alone module to provide information about whether optional deps exist.
"""
from importlib import import_module
import logging
import sys
logger = logging.getLogger(__name__)
_not_importable = set()
def get_module(name, should_load=True):
"""
Return module or None. Absolute import is required.
:param (str) name: Dot-separated module path. E.g., 'scipy.stats'.
:raise: (ImportError) Only when exc_msg is defined.
:return: (module|None) If import succeeds, the module will be returned.
"""
if not should_load:
return sys.modules.get(name, None)
if name not in _not_importable:
try:
return import_module(name)
except ImportError:
_not_importable.add(name)
except Exception:
_not_importable.add(name)
msg = f"Error importing optional module {name}"
logger.exception(msg)
return None

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,557 @@
import base64
import decimal
import json as _json
import sys
import re
from functools import reduce
from _plotly_utils.optional_imports import get_module
from _plotly_utils.basevalidators import (
ImageUriValidator,
copy_to_readonly_numpy_array,
is_homogeneous_array,
)
int8min = -128
int8max = 127
int16min = -32768
int16max = 32767
int32min = -2147483648
int32max = 2147483647
uint8max = 255
uint16max = 65535
uint32max = 4294967295
plotlyjsShortTypes = {
"int8": "i1",
"uint8": "u1",
"int16": "i2",
"uint16": "u2",
"int32": "i4",
"uint32": "u4",
"float32": "f4",
"float64": "f8",
}
def to_typed_array_spec(v):
"""
Convert numpy array to plotly.js typed array spec
If not possible return the original value
"""
v = copy_to_readonly_numpy_array(v)
# Skip b64 encoding if numpy is not installed,
# or if v is not a numpy array, or if v is empty
np = get_module("numpy", should_load=False)
if not np or not isinstance(v, np.ndarray) or v.size == 0:
return v
dtype = str(v.dtype)
# convert default Big Ints until we could support them in plotly.js
if dtype == "int64":
max = v.max()
min = v.min()
if max <= int8max and min >= int8min:
v = v.astype("int8")
elif max <= int16max and min >= int16min:
v = v.astype("int16")
elif max <= int32max and min >= int32min:
v = v.astype("int32")
else:
return v
elif dtype == "uint64":
max = v.max()
min = v.min()
if max <= uint8max and min >= 0:
v = v.astype("uint8")
elif max <= uint16max and min >= 0:
v = v.astype("uint16")
elif max <= uint32max and min >= 0:
v = v.astype("uint32")
else:
return v
dtype = str(v.dtype)
if dtype in plotlyjsShortTypes:
arrObj = {
"dtype": plotlyjsShortTypes[dtype],
"bdata": base64.b64encode(v).decode("ascii"),
}
if v.ndim > 1:
arrObj["shape"] = str(v.shape)[1:-1]
return arrObj
return v
def is_skipped_key(key):
"""
Return whether the key is skipped for conversion to the typed array spec
"""
skipped_keys = ["geojson", "layer", "layers", "range"]
return any(skipped_key == key for skipped_key in skipped_keys)
def convert_to_base64(obj):
if isinstance(obj, dict):
for key, value in obj.items():
if is_skipped_key(key):
continue
elif is_homogeneous_array(value):
obj[key] = to_typed_array_spec(value)
else:
convert_to_base64(value)
elif isinstance(obj, list) or isinstance(obj, tuple):
for value in obj:
convert_to_base64(value)
def cumsum(x):
"""
Custom cumsum to avoid a numpy import.
"""
def _reducer(a, x):
if len(a) == 0:
return [x]
return a + [a[-1] + x]
ret = reduce(_reducer, x, [])
return ret
class PlotlyJSONEncoder(_json.JSONEncoder):
"""
Meant to be passed as the `cls` kwarg to json.dumps(obj, cls=..)
See PlotlyJSONEncoder.default for more implementation information.
Additionally, this encoder overrides nan functionality so that 'Inf',
'NaN' and '-Inf' encode to 'null'. Which is stricter JSON than the Python
version.
"""
def coerce_to_strict(self, const):
"""
This is used to ultimately *encode* into strict JSON, see `encode`
"""
# before python 2.7, 'true', 'false', 'null', were include here.
if const in ("Infinity", "-Infinity", "NaN"):
return None
else:
return const
def encode(self, o):
"""
Load and then dump the result using parse_constant kwarg
Note that setting invalid separators will cause a failure at this step.
"""
# this will raise errors in a normal-expected way
encoded_o = super(PlotlyJSONEncoder, self).encode(o)
# Brute force guessing whether NaN or Infinity values are in the string
# We catch false positive cases (e.g. strings such as titles, labels etc.)
# but this is ok since the intention is to skip the decoding / reencoding
# step when it's completely safe
if not ("NaN" in encoded_o or "Infinity" in encoded_o):
return encoded_o
# now:
# 1. `loads` to switch Infinity, -Infinity, NaN to None
# 2. `dumps` again so you get 'null' instead of extended JSON
try:
new_o = _json.loads(encoded_o, parse_constant=self.coerce_to_strict)
except ValueError:
# invalid separators will fail here. raise a helpful exception
raise ValueError(
"Encoding into strict JSON failed. Did you set the separators "
"valid JSON separators?"
)
else:
return _json.dumps(
new_o,
sort_keys=self.sort_keys,
indent=self.indent,
separators=(self.item_separator, self.key_separator),
)
def default(self, obj):
"""
Accept an object (of unknown type) and try to encode with priority:
1. builtin: user-defined objects
2. sage: sage math cloud
3. pandas: dataframes/series
4. numpy: ndarrays
5. datetime: time/datetime objects
Each method throws a NotEncoded exception if it fails.
The default method will only get hit if the object is not a type that
is naturally encoded by json:
Normal objects:
dict object
list, tuple array
str, unicode string
int, long, float number
True true
False false
None null
Extended objects:
float('nan') 'NaN'
float('infinity') 'Infinity'
float('-infinity') '-Infinity'
Therefore, we only anticipate either unknown iterables or values here.
"""
# TODO: The ordering if these methods is *very* important. Is this OK?
encoding_methods = (
self.encode_as_plotly,
self.encode_as_sage,
self.encode_as_numpy,
self.encode_as_pandas,
self.encode_as_datetime,
self.encode_as_date,
self.encode_as_list, # because some values have `tolist` do last.
self.encode_as_decimal,
self.encode_as_pil,
)
for encoding_method in encoding_methods:
try:
return encoding_method(obj)
except NotEncodable:
pass
return _json.JSONEncoder.default(self, obj)
@staticmethod
def encode_as_plotly(obj):
"""Attempt to use a builtin `to_plotly_json` method."""
try:
return obj.to_plotly_json()
except AttributeError:
raise NotEncodable
@staticmethod
def encode_as_list(obj):
"""Attempt to use `tolist` method to convert to normal Python list."""
if hasattr(obj, "tolist"):
return obj.tolist()
else:
raise NotEncodable
@staticmethod
def encode_as_sage(obj):
"""Attempt to convert sage.all.RR to floats and sage.all.ZZ to ints"""
sage_all = get_module("sage.all")
if not sage_all:
raise NotEncodable
if obj in sage_all.RR:
return float(obj)
elif obj in sage_all.ZZ:
return int(obj)
else:
raise NotEncodable
@staticmethod
def encode_as_pandas(obj):
"""Attempt to convert pandas.NaT / pandas.NA"""
pandas = get_module("pandas", should_load=False)
if not pandas:
raise NotEncodable
if obj is pandas.NaT:
return None
# pandas.NA was introduced in pandas 1.0
if hasattr(pandas, "NA") and obj is pandas.NA:
return None
raise NotEncodable
@staticmethod
def encode_as_numpy(obj):
"""Attempt to convert numpy.ma.core.masked"""
numpy = get_module("numpy", should_load=False)
if not numpy:
raise NotEncodable
if obj is numpy.ma.core.masked:
return float("nan")
elif isinstance(obj, numpy.ndarray) and obj.dtype.kind == "M":
try:
return numpy.datetime_as_string(obj).tolist()
except TypeError:
pass
raise NotEncodable
@staticmethod
def encode_as_datetime(obj):
"""Convert datetime objects to iso-format strings"""
try:
return obj.isoformat()
except AttributeError:
raise NotEncodable
@staticmethod
def encode_as_date(obj):
"""Attempt to convert to utc-iso time string using date methods."""
try:
time_string = obj.isoformat()
except AttributeError:
raise NotEncodable
else:
return iso_to_plotly_time_string(time_string)
@staticmethod
def encode_as_decimal(obj):
"""Attempt to encode decimal by converting it to float"""
if isinstance(obj, decimal.Decimal):
return float(obj)
else:
raise NotEncodable
@staticmethod
def encode_as_pil(obj):
"""Attempt to convert PIL.Image.Image to base64 data uri"""
image = get_module("PIL.Image")
if image is not None and isinstance(obj, image.Image):
return ImageUriValidator.pil_image_to_uri(obj)
else:
raise NotEncodable
class NotEncodable(Exception):
pass
def iso_to_plotly_time_string(iso_string):
"""Remove timezone info and replace 'T' delimeter with ' ' (ws)."""
# make sure we don't send timezone info to plotly
if (iso_string.split("-")[:3] == "00:00") or (iso_string.split("+")[0] == "00:00"):
raise Exception(
"Plotly won't accept timestrings with timezone info.\n"
"All timestrings are assumed to be in UTC."
)
iso_string = iso_string.replace("-00:00", "").replace("+00:00", "")
if iso_string.endswith("T00:00:00"):
return iso_string.replace("T00:00:00", "")
else:
return iso_string.replace("T", " ")
def template_doc(**names):
def _decorator(func):
if not sys.version_info[:2] == (3, 2):
if func.__doc__ is not None:
func.__doc__ = func.__doc__.format(**names)
return func
return _decorator
def _natural_sort_strings(vals, reverse=False):
def key(v):
v_parts = re.split(r"(\d+)", v)
for i in range(len(v_parts)):
try:
v_parts[i] = int(v_parts[i])
except ValueError:
# not an int
pass
return tuple(v_parts)
return sorted(vals, key=key, reverse=reverse)
def _get_int_type():
np = get_module("numpy", should_load=False)
if np:
int_type = (int, np.integer)
else:
int_type = (int,)
return int_type
def split_multichar(ss, chars):
"""
Split all the strings in ss at any of the characters in chars.
Example:
>>> ss = ["a.string[0].with_separators"]
>>> chars = list(".[]_")
>>> split_multichar(ss, chars)
['a', 'string', '0', '', 'with', 'separators']
:param (list) ss: A list of strings.
:param (list) chars: Is a list of chars (note: not a string).
"""
if len(chars) == 0:
return ss
c = chars.pop()
ss = reduce(lambda x, y: x + y, map(lambda x: x.split(c), ss))
return split_multichar(ss, chars)
def split_string_positions(ss):
"""
Given a list of strings split using split_multichar, return a list of
integers representing the indices of the first character of every string in
the original string.
Example:
>>> ss = ["a.string[0].with_separators"]
>>> chars = list(".[]_")
>>> ss_split = split_multichar(ss, chars)
>>> ss_split
['a', 'string', '0', '', 'with', 'separators']
>>> split_string_positions(ss_split)
[0, 2, 9, 11, 12, 17]
:param (list) ss: A list of strings.
"""
return list(
map(
lambda t: t[0] + t[1],
zip(range(len(ss)), cumsum([0] + list(map(len, ss[:-1])))),
)
)
def display_string_positions(p, i=None, offset=0, length=1, char="^", trim=True):
"""
Return a string that is whitespace except at p[i] which is replaced with char.
If i is None then all the indices of the string in p are replaced with char.
Example:
>>> ss = ["a.string[0].with_separators"]
>>> chars = list(".[]_")
>>> ss_split = split_multichar(ss, chars)
>>> ss_split
['a', 'string', '0', '', 'with', 'separators']
>>> ss_pos = split_string_positions(ss_split)
>>> ss[0]
'a.string[0].with_separators'
>>> display_string_positions(ss_pos,4)
' ^'
>>> display_string_positions(ss_pos,4,offset=1,length=3,char="~",trim=False)
' ~~~ '
>>> display_string_positions(ss_pos)
'^ ^ ^ ^^ ^'
:param (list) p: A list of integers.
:param (integer|None) i: Optional index of p to display.
:param (integer) offset: Allows adding a number of spaces to the replacement.
:param (integer) length: Allows adding a replacement that is the char
repeated length times.
:param (str) char: allows customizing the replacement character.
:param (boolean) trim: trims the remaining whitespace if True.
"""
s = [" " for _ in range(max(p) + 1 + offset + length)]
maxaddr = 0
if i is None:
for p_ in p:
for temp in range(length):
maxaddr = p_ + offset + temp
s[maxaddr] = char
else:
for temp in range(length):
maxaddr = p[i] + offset + temp
s[maxaddr] = char
ret = "".join(s)
if trim:
ret = ret[: maxaddr + 1]
return ret
def chomp_empty_strings(strings, c, reverse=False):
"""
Given a list of strings, some of which are the empty string "", replace the
empty strings with c and combine them with the closest non-empty string on
the left or "" if it is the first string.
Examples:
for c="_"
['hey', '', 'why', '', '', 'whoa', '', ''] -> ['hey_', 'why__', 'whoa__']
['', 'hi', '', "I'm", 'bob', '', ''] -> ['_', 'hi_', "I'm", 'bob__']
['hi', "i'm", 'a', 'good', 'string'] -> ['hi', "i'm", 'a', 'good', 'string']
Some special cases are:
[] -> []
[''] -> ['']
['', ''] -> ['_']
['', '', '', ''] -> ['___']
If reverse is true, empty strings are combined with closest non-empty string
on the right or "" if it is the last string.
"""
def _rev(vals):
return [s[::-1] for s in vals][::-1]
if reverse:
return _rev(chomp_empty_strings(_rev(strings), c))
if not len(strings):
return strings
if sum(map(len, strings)) == 0:
return [c * (len(strings) - 1)]
class _Chomper:
def __init__(self, c):
self.c = c
def __call__(self, x, y):
# x is list up to now
# y is next item in list
# x should be [""] initially, and then empty strings filtered out at the
# end
if len(y) == 0:
return x[:-1] + [x[-1] + self.c]
else:
return x + [y]
return list(filter(len, reduce(_Chomper(c), strings, [""])))
# taken from
# https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#Python
def levenshtein(s1, s2):
if len(s1) < len(s2):
return levenshtein(s2, s1) # len(s1) >= len(s2)
if len(s2) == 0:
return len(s1)
previous_row = range(len(s2) + 1)
for i, c1 in enumerate(s1):
current_row = [i + 1]
for j, c2 in enumerate(s2):
# j+1 instead of j since previous_row and current_row are one character longer
# than s2
insertions = previous_row[j + 1] + 1
deletions = current_row[j] + 1
substitutions = previous_row[j] + (c1 != c2)
current_row.append(min(insertions, deletions, substitutions))
previous_row = current_row
return previous_row[-1]
def find_closest_string(string, strings):
def _key(s):
# sort by levenshtein distance and lexographically to maintain a stable
# sort for different keys with the same levenshtein distance
return (levenshtein(s, string), s)
return sorted(strings, key=_key)[0]

View File

@ -0,0 +1,33 @@
# This is a stub package designed to roughly emulate the _yaml
# extension module, which previously existed as a standalone module
# and has been moved into the `yaml` package namespace.
# It does not perfectly mimic its old counterpart, but should get
# close enough for anyone who's relying on it even when they shouldn't.
import yaml
# in some circumstances, the yaml module we imoprted may be from a different version, so we need
# to tread carefully when poking at it here (it may not have the attributes we expect)
if not getattr(yaml, '__with_libyaml__', False):
from sys import version_info
exc = ModuleNotFoundError if version_info >= (3, 6) else ImportError
raise exc("No module named '_yaml'")
else:
from yaml._yaml import *
import warnings
warnings.warn(
'The _yaml extension module is now located at yaml._yaml'
' and its location is subject to change. To use the'
' LibYAML-based parser and emitter, import from `yaml`:'
' `from yaml import CLoader as Loader, CDumper as Dumper`.',
DeprecationWarning
)
del warnings
# Don't `del yaml` here because yaml is actually an existing
# namespace member of _yaml.
__name__ = '_yaml'
# If the module is top-level (i.e. not a part of any specific package)
# then the attribute should be set to ''.
# https://docs.python.org/3.8/library/types.html
__package__ = ''

View File

@ -0,0 +1 @@
uv

View File

@ -0,0 +1,27 @@
Copyright (c) 2013-2025 by the Babel Team, see AUTHORS for more information.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,54 @@
Metadata-Version: 2.2
Name: babel
Version: 2.17.0
Summary: Internationalization utilities
Home-page: https://babel.pocoo.org/
Author: Armin Ronacher
Author-email: armin.ronacher@active-4.com
Maintainer: Aarni Koskela
Maintainer-email: akx@iki.fi
License: BSD-3-Clause
Project-URL: Source, https://github.com/python-babel/babel
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.8
License-File: LICENSE
Requires-Dist: pytz>=2015.7; python_version < "3.9"
Provides-Extra: dev
Requires-Dist: tzdata; sys_platform == "win32" and extra == "dev"
Requires-Dist: backports.zoneinfo; python_version < "3.9" and extra == "dev"
Requires-Dist: freezegun~=1.0; extra == "dev"
Requires-Dist: jinja2>=3.0; extra == "dev"
Requires-Dist: pytest-cov; extra == "dev"
Requires-Dist: pytest>=6.0; extra == "dev"
Requires-Dist: pytz; extra == "dev"
Requires-Dist: setuptools; extra == "dev"
Dynamic: author
Dynamic: author-email
Dynamic: classifier
Dynamic: description
Dynamic: home-page
Dynamic: license
Dynamic: maintainer
Dynamic: maintainer-email
Dynamic: project-url
Dynamic: provides-extra
Dynamic: requires-dist
Dynamic: requires-python
Dynamic: summary
A collection of tools for internationalizing Python applications.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,5 @@
Wheel-Version: 1.0
Generator: setuptools (75.8.0)
Root-Is-Purelib: true
Tag: py3-none-any

View File

@ -0,0 +1,20 @@
[babel.checkers]
num_plurals = babel.messages.checkers:num_plurals
python_format = babel.messages.checkers:python_format
[babel.extractors]
ignore = babel.messages.extract:extract_nothing
javascript = babel.messages.extract:extract_javascript
python = babel.messages.extract:extract_python
[console_scripts]
pybabel = babel.messages.frontend:main
[distutils.commands]
compile_catalog = babel.messages.setuptools_frontend:compile_catalog
extract_messages = babel.messages.setuptools_frontend:extract_messages
init_catalog = babel.messages.setuptools_frontend:init_catalog
update_catalog = babel.messages.setuptools_frontend:update_catalog
[distutils.setup_keywords]
message_extractors = babel.messages.setuptools_frontend:check_message_extractors

View File

@ -0,0 +1 @@
babel

View File

@ -0,0 +1,38 @@
"""
babel
~~~~~
Integrated collection of utilities that assist in internationalizing and
localizing applications.
This package is basically composed of two major parts:
* tools to build and work with ``gettext`` message catalogs
* a Python interface to the CLDR (Common Locale Data Repository), providing
access to various locale display names, localized number and date
formatting, etc.
:copyright: (c) 2013-2025 by the Babel Team.
:license: BSD, see LICENSE for more details.
"""
from babel.core import (
Locale,
UnknownLocaleError,
default_locale,
get_locale_identifier,
negotiate_locale,
parse_locale,
)
__version__ = '2.17.0'
__all__ = [
'Locale',
'UnknownLocaleError',
'__version__',
'default_locale',
'get_locale_identifier',
'negotiate_locale',
'parse_locale',
]

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -0,0 +1,72 @@
from __future__ import annotations
from babel.core import get_global
def get_official_languages(territory: str, regional: bool = False, de_facto: bool = False) -> tuple[str, ...]:
"""
Get the official language(s) for the given territory.
The language codes, if any are known, are returned in order of descending popularity.
If the `regional` flag is set, then languages which are regionally official are also returned.
If the `de_facto` flag is set, then languages which are "de facto" official are also returned.
.. warning:: Note that the data is as up to date as the current version of the CLDR used
by Babel. If you need scientifically accurate information, use another source!
:param territory: Territory code
:type territory: str
:param regional: Whether to return regionally official languages too
:type regional: bool
:param de_facto: Whether to return de-facto official languages too
:type de_facto: bool
:return: Tuple of language codes
:rtype: tuple[str]
"""
territory = str(territory).upper()
allowed_stati = {"official"}
if regional:
allowed_stati.add("official_regional")
if de_facto:
allowed_stati.add("de_facto_official")
languages = get_global("territory_languages").get(territory, {})
pairs = [
(info['population_percent'], language)
for language, info in languages.items()
if info.get('official_status') in allowed_stati
]
pairs.sort(reverse=True)
return tuple(lang for _, lang in pairs)
def get_territory_language_info(territory: str) -> dict[str, dict[str, float | str | None]]:
"""
Get a dictionary of language information for a territory.
The dictionary is keyed by language code; the values are dicts with more information.
The following keys are currently known for the values:
* `population_percent`: The percentage of the territory's population speaking the
language.
* `official_status`: An optional string describing the officiality status of the language.
Known values are "official", "official_regional" and "de_facto_official".
.. warning:: Note that the data is as up to date as the current version of the CLDR used
by Babel. If you need scientifically accurate information, use another source!
.. note:: Note that the format of the dict returned may change between Babel versions.
See https://www.unicode.org/cldr/charts/latest/supplemental/territory_language_information.html
:param territory: Territory code
:type territory: str
:return: Language information dictionary
:rtype: dict[str, dict]
"""
territory = str(territory).upper()
return get_global("territory_languages").get(territory, {}).copy()

View File

@ -0,0 +1,132 @@
"""
babel.lists
~~~~~~~~~~~
Locale dependent formatting of lists.
The default locale for the functions in this module is determined by the
following environment variables, in that order:
* ``LC_ALL``, and
* ``LANG``
:copyright: (c) 2015-2025 by the Babel Team.
:license: BSD, see LICENSE for more details.
"""
from __future__ import annotations
import warnings
from collections.abc import Sequence
from typing import Literal
from babel.core import Locale, default_locale
_DEFAULT_LOCALE = default_locale() # TODO(3.0): Remove this.
def __getattr__(name):
if name == "DEFAULT_LOCALE":
warnings.warn(
"The babel.lists.DEFAULT_LOCALE constant is deprecated and will be removed.",
DeprecationWarning,
stacklevel=2,
)
return _DEFAULT_LOCALE
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
def format_list(
lst: Sequence[str],
style: Literal['standard', 'standard-short', 'or', 'or-short', 'unit', 'unit-short', 'unit-narrow'] = 'standard',
locale: Locale | str | None = None,
) -> str:
"""
Format the items in `lst` as a list.
>>> format_list(['apples', 'oranges', 'pears'], locale='en')
u'apples, oranges, and pears'
>>> format_list(['apples', 'oranges', 'pears'], locale='zh')
u'apples\u3001oranges\u548cpears'
>>> format_list(['omena', 'peruna', 'aplari'], style='or', locale='fi')
u'omena, peruna tai aplari'
Not all styles are necessarily available in all locales.
The function will attempt to fall back to replacement styles according to the rules
set forth in the CLDR root XML file, and raise a ValueError if no suitable replacement
can be found.
The following text is verbatim from the Unicode TR35-49 spec [1].
* standard:
A typical 'and' list for arbitrary placeholders.
eg. "January, February, and March"
* standard-short:
A short version of an 'and' list, suitable for use with short or abbreviated placeholder values.
eg. "Jan., Feb., and Mar."
* or:
A typical 'or' list for arbitrary placeholders.
eg. "January, February, or March"
* or-short:
A short version of an 'or' list.
eg. "Jan., Feb., or Mar."
* unit:
A list suitable for wide units.
eg. "3 feet, 7 inches"
* unit-short:
A list suitable for short units
eg. "3 ft, 7 in"
* unit-narrow:
A list suitable for narrow units, where space on the screen is very limited.
eg. "3 7″"
[1]: https://www.unicode.org/reports/tr35/tr35-49/tr35-general.html#ListPatterns
:param lst: a sequence of items to format in to a list
:param style: the style to format the list with. See above for description.
:param locale: the locale. Defaults to the system locale.
"""
locale = Locale.parse(locale or _DEFAULT_LOCALE)
if not lst:
return ''
if len(lst) == 1:
return lst[0]
patterns = _resolve_list_style(locale, style)
if len(lst) == 2 and '2' in patterns:
return patterns['2'].format(*lst)
result = patterns['start'].format(lst[0], lst[1])
for elem in lst[2:-1]:
result = patterns['middle'].format(result, elem)
result = patterns['end'].format(result, lst[-1])
return result
# Based on CLDR 45's root.xml file's `<alias>`es.
# The root file defines both `standard` and `or`,
# so they're always available.
# TODO: It would likely be better to use the
# babel.localedata.Alias mechanism for this,
# but I'm not quite sure how it's supposed to
# work with inheritance and data in the root.
_style_fallbacks = {
"or-narrow": ["or-short", "or"],
"or-short": ["or"],
"standard-narrow": ["standard-short", "standard"],
"standard-short": ["standard"],
"unit": ["unit-short", "standard"],
"unit-narrow": ["unit-short", "unit", "standard"],
"unit-short": ["standard"],
}
def _resolve_list_style(locale: Locale, style: str):
for style in (style, *(_style_fallbacks.get(style, []))): # noqa: B020
if style in locale.list_patterns:
return locale.list_patterns[style]
raise ValueError(
f"Locale {locale} does not support list formatting style {style!r} "
f"(supported are {sorted(locale.list_patterns)})",
)

View File

@ -0,0 +1,41 @@
UNICODE LICENSE V3
COPYRIGHT AND PERMISSION NOTICE
Copyright © 2004-2025 Unicode, Inc.
NOTICE TO USER: Carefully read the following legal agreement. BY
DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR
SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE
TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT
DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE.
Permission is hereby granted, free of charge, to any person obtaining a
copy of data files and any associated documentation (the "Data Files") or
software and any associated documentation (the "Software") to deal in the
Data Files or Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, and/or sell
copies of the Data Files or Software, and to permit persons to whom the
Data Files or Software are furnished to do so, provided that either (a)
this copyright and permission notice appear with all copies of the Data
Files or Software, or (b) this copyright and permission notice appear in
associated Documentation.
THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
THIRD PARTY RIGHTS.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE
BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES,
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA
FILES OR SOFTWARE.
Except as contained in this notice, the name of a copyright holder shall
not be used in advertising or otherwise to promote the sale, use or other
dealings in these Data Files or Software without prior written
authorization of the copyright holder.
SPDX-License-Identifier: Unicode-3.0

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More