From 386cc5dc77a21181ec2d99c5e8a5ceb837c25a32 Mon Sep 17 00:00:00 2001 From: Philippe Pinard Date: Sun, 10 May 2020 11:09:55 +0100 Subject: [PATCH 1/8] Add pre-commit, GitHub action. Update setup script, gitignore. Remove Eclipse project files, Travis/Appveyor CI. --- .github/workflows/ci.yml | 42 ++ .gitignore | 224 +++++++++- .pre-commit-config.yaml | 11 + .project | 17 - .pydevproject.sample | 9 - .travis.yml | 17 - LICENSE | 7 +- README.rst | 4 +- appveyor.yml | 15 - pyxray/__init__.py | 5 +- pyxray/_version.py | 300 ++++++++----- requirements-dev.txt | 2 + requirements-test.txt | 2 + setup.py | 130 +++--- versioneer.py | 928 +++++++++++++++++++++++---------------- 15 files changed, 1090 insertions(+), 623 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .pre-commit-config.yaml delete mode 100644 .project delete mode 100644 .pydevproject.sample delete mode 100644 .travis.yml delete mode 100644 appveyor.yml create mode 100644 requirements-dev.txt create mode 100644 requirements-test.txt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..899a306 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,42 @@ +name: CI + +on: + push: + branches: [master] + pull_request: + branches: [master] + schedule: + - cron: '0 0 1 * *' + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest] + python-version: [3.x] + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + - name: Cache pip + uses: actions/cache@v1 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + ${{ runner.os }}- + - name: Install dependencies + run: | + python -m pip install --upgrade pip codecov + pip install -r requirements-test.txt + pip install --upgrade -e . + python setup.py build + - name: Test with pytest + run: pytest + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v1 diff --git a/.gitignore b/.gitignore index 548c81a..2eb46bb 100644 --- a/.gitignore +++ b/.gitignore @@ -3,15 +3,32 @@ pyxray/data/*.db pyxray/data/*.db* cover -#### joe made this: https://goel.io/joe +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ -#####=== PyCharm ===##### -.idea +#####=== Jython ===##### -#####=== Eclipse ===##### -*.pydevproject +*.pyc +*.class + +# Created by https://www.gitignore.io/api/code,python,pycharm,eclipse +# Edit at https://www.gitignore.io/?templates=code,python,pycharm,eclipse + +### Code ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +### Eclipse ### .metadata -.gradle bin/ tmp/ *.tmp @@ -21,6 +38,7 @@ tmp/ local.properties .settings/ .loadpath +.recommenders # External tool builders .externalToolBuilders/ @@ -28,43 +46,173 @@ local.properties # Locally stored "Eclipse launch configurations" *.launch -# CDT-specific +# PyDev specific (Python IDE for Eclipse) +*.pydevproject + +# CDT-specific (C/C++ Development Tooling) .cproject -# PDT-specific +# CDT- autotools +.autotools + +# Java annotation processor (APT) +.factorypath + +# PDT-specific (PHP Development Tools) .buildpath # sbteclipse plugin .target +# Tern plugin +.tern-project + # TeXlipse plugin .texlipse -#####=== Python ===##### +# STS (Spring Tool Suite) +.springBeans + +# Code Recommenders +.recommenders/ + +# Annotation Processing +.apt_generated/ + +# Scala IDE specific (Scala & Java development for Eclipse) +.cache-main +.scala_dependencies +.worksheet + +### Eclipse Patch ### +# Eclipse Core +.project + +# JDT-specific (Eclipse Java Development Tools) +.classpath + +# Annotation Processing +.apt_generated + +.sts4-cache/ + +### PyCharm ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### PyCharm Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +.idea/**/sonarlint/ + +# SonarQube Plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator/ + +### Python ### # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] +*$py.class # C extensions *.so # Distribution / packaging .Python -env/ build/ develop-eggs/ dist/ downloads/ eggs/ +.eggs/ lib/ lib64/ parts/ sdist/ var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ *.egg-info/ .installed.cfg *.egg +MANIFEST # PyInstaller # Usually these files are written by a python script from a template @@ -79,17 +227,22 @@ pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ +.nox/ .coverage +.coverage.* .cache nosetests.xml coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ # Translations *.mo *.pot -# Django stuff: -*.log +# Scrapy stuff: +.scrapy # Sphinx documentation docs/_build/ @@ -97,17 +250,42 @@ docs/_build/ # PyBuilder target/ -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ +# pyenv +.python-version -#####=== Jython ===##### +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock -*.pyc -*.class +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# Mr Developer +.mr.developer.cfg +.pydevproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ +# End of https://www.gitignore.io/api/code,python,pycharm,eclipse diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..f7e6ca4 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,11 @@ +repos: + - repo: https://github.com/psf/black + rev: stable + hooks: + - id: black + language_version: python3.7 + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v2.4.0 + hooks: + - id: requirements-txt-fixer + - id: check-yaml \ No newline at end of file diff --git a/.project b/.project deleted file mode 100644 index 62fdfd0..0000000 --- a/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - pyxray - - - - - - org.python.pydev.PyDevBuilder - - - - - - org.python.pydev.pythonNature - - diff --git a/.pydevproject.sample b/.pydevproject.sample deleted file mode 100644 index 55ac9b1..0000000 --- a/.pydevproject.sample +++ /dev/null @@ -1,9 +0,0 @@ - - - -python 3.0 -Default - -/${PROJECT_DIR_NAME} - - diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 921a1e4..0000000 --- a/.travis.yml +++ /dev/null @@ -1,17 +0,0 @@ -sudo: false -dist: xenial -language: python -cache: pip -python: - - '3.6' - - '3.7' -install: -- pip install --upgrade pip codecov -- pip install --upgrade -e .[build,develop] -- python3 setup.py build -script: -- pytest -after_success: -- codecov -notifications: - email: false diff --git a/LICENSE b/LICENSE index 697fa6c..4d5717f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ -The MIT License (MIT) +MIT License Copyright (c) 2015-2016/06 Philippe Pinard and Silvia Richter -Copyright (c) 2016/06-2017 Philippe Pinard +Copyright (c) 2016/06-2020 Philippe Pinard Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -19,4 +19,5 @@ 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. \ No newline at end of file +SOFTWARE. + diff --git a/README.rst b/README.rst index ad31da9..0eebe54 100644 --- a/README.rst +++ b/README.rst @@ -5,8 +5,8 @@ pyxray .. image:: https://img.shields.io/pypi/v/pyxray.svg :target: https://pypi.python.org/pypi/pyxray -.. image:: https://img.shields.io/travis/openmicroanalysis/pyxray.svg - :target: https://travis-ci.org/openmicroanalysis/pyxray +.. image:: https://img.shields.io/github/workflow/status/openmicroanalysis/pyxray/CI + :alt: GitHub Workflow Status .. image:: https://img.shields.io/codecov/c/github/openmicroanalysis/pyxray.svg :target: https://codecov.io/github/openmicroanalysis/pyxray diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index efd3db3..0000000 --- a/appveyor.yml +++ /dev/null @@ -1,15 +0,0 @@ -environment: - - matrix: - - PYTHON: "C:\\Python36-x64" - - PYTHON: "C:\\Python37-x64" - -install: - - "%PYTHON%\\python.exe -m pip install --upgrade pip wheel codecov" - - "%PYTHON%\\python.exe -m pip install --upgrade -e .[build,develop]" - - "%PYTHON%\\python.exe setup.py build" - -build: off - -test_script: - - "%PYTHON%\\Scripts\\pytest.exe" \ No newline at end of file diff --git a/pyxray/__init__.py b/pyxray/__init__.py index 0a793df..741e234 100644 --- a/pyxray/__init__.py +++ b/pyxray/__init__.py @@ -2,8 +2,9 @@ pyxray - Definitions and properties of X-ray transitions """ -from pyxray._version import get_versions -__version__ = get_versions()['version'] +from ._version import get_versions + +__version__ = get_versions()["version"] del get_versions from pyxray.base import NotFound diff --git a/pyxray/_version.py b/pyxray/_version.py index 0c97d8d..cc18786 100644 --- a/pyxray/_version.py +++ b/pyxray/_version.py @@ -1,4 +1,3 @@ - # This file helps to compute a version number in source trees obtained from # git-archive tarball (such as those provided by githubs download-from-tag # feature). Distribution tarballs (built by setup.py sdist) and build @@ -6,7 +5,9 @@ # that just contains the computed version number. # This file is released into the public domain. Generated by -# versioneer-0.15 (https://github.com/warner/python-versioneer) +# versioneer-0.18 (https://github.com/warner/python-versioneer) + +"""Git implementation of _version.py.""" import errno import os @@ -16,21 +17,24 @@ def get_keywords(): + """Get the keywords needed to look up the version information.""" # these strings will be replaced by git during git-archive. # setup.py/versioneer.py will grep for the variable names, so they must # each be defined on a line of their own. _version.py will just call # get_keywords(). git_refnames = "$Format:%d$" git_full = "$Format:%H$" - keywords = {"refnames": git_refnames, "full": git_full} + git_date = "$Format:%ci$" + keywords = {"refnames": git_refnames, "full": git_full, "date": git_date} return keywords class VersioneerConfig: - pass + """Container for Versioneer configuration parameters.""" def get_config(): + """Create, populate and return the VersioneerConfig() object.""" # these strings are filled in when 'setup.py versioneer' creates # _version.py cfg = VersioneerConfig() @@ -44,7 +48,7 @@ def get_config(): class NotThisMethod(Exception): - pass + """Exception raised if a method is not valid for the current scenario.""" LONG_VERSION_PY = {} @@ -52,24 +56,33 @@ class NotThisMethod(Exception): def register_vcs_handler(vcs, method): # decorator + """Decorator to mark a method as the handler for a particular VCS.""" + def decorate(f): + """Store f in HANDLERS[vcs][method].""" if vcs not in HANDLERS: HANDLERS[vcs] = {} HANDLERS[vcs][method] = f return f + return decorate -def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False): +def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, env=None): + """Call the given command(s).""" assert isinstance(commands, list) p = None for c in commands: try: dispcmd = str([c] + args) # remember shell=False, so use git.cmd on windows, not just git - p = subprocess.Popen([c] + args, cwd=cwd, stdout=subprocess.PIPE, - stderr=(subprocess.PIPE if hide_stderr - else None)) + p = subprocess.Popen( + [c] + args, + cwd=cwd, + env=env, + stdout=subprocess.PIPE, + stderr=(subprocess.PIPE if hide_stderr else None), + ) break except EnvironmentError: e = sys.exc_info()[1] @@ -78,37 +91,56 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False): if verbose: print("unable to run %s" % dispcmd) print(e) - return None + return None, None else: if verbose: print("unable to find command, tried %s" % (commands,)) - return None + return None, None stdout = p.communicate()[0].strip() if sys.version_info[0] >= 3: stdout = stdout.decode() if p.returncode != 0: if verbose: print("unable to run %s (error)" % dispcmd) - return None - return stdout + print("stdout was %s" % stdout) + return None, p.returncode + return stdout, p.returncode def versions_from_parentdir(parentdir_prefix, root, verbose): - # Source tarballs conventionally unpack into a directory that includes - # both the project name and a version string. - dirname = os.path.basename(root) - if not dirname.startswith(parentdir_prefix): - if verbose: - print("guessing rootdir is '%s', but '%s' doesn't start with " - "prefix '%s'" % (root, dirname, parentdir_prefix)) - raise NotThisMethod("rootdir doesn't start with parentdir_prefix") - return {"version": dirname[len(parentdir_prefix):], - "full-revisionid": None, - "dirty": False, "error": None} + """Try to determine the version from the parent directory name. + + Source tarballs conventionally unpack into a directory that includes both + the project name and a version string. We will also support searching up + two directory levels for an appropriately named parent directory + """ + rootdirs = [] + + for i in range(3): + dirname = os.path.basename(root) + if dirname.startswith(parentdir_prefix): + return { + "version": dirname[len(parentdir_prefix) :], + "full-revisionid": None, + "dirty": False, + "error": None, + "date": None, + } + else: + rootdirs.append(root) + root = os.path.dirname(root) # up a level + + if verbose: + print( + "Tried directories %s but none started with prefix %s" + % (str(rootdirs), parentdir_prefix) + ) + raise NotThisMethod("rootdir doesn't start with parentdir_prefix") @register_vcs_handler("git", "get_keywords") def git_get_keywords(versionfile_abs): + """Extract version information from the given file.""" # the code embedded in _version.py can just fetch the value of these # keywords. When used from setup.py, we don't want to import _version.py, # so we do it with a regexp instead. This function is not used from @@ -125,6 +157,10 @@ def git_get_keywords(versionfile_abs): mo = re.search(r'=\s*"(.*)"', line) if mo: keywords["full"] = mo.group(1) + if line.strip().startswith("git_date ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["date"] = mo.group(1) f.close() except EnvironmentError: pass @@ -133,8 +169,18 @@ def git_get_keywords(versionfile_abs): @register_vcs_handler("git", "keywords") def git_versions_from_keywords(keywords, tag_prefix, verbose): + """Get version information from git keywords.""" if not keywords: raise NotThisMethod("no keywords at all, weird") + date = keywords.get("date") + if date is not None: + # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant + # datestamp. However we prefer "%ci" (which expands to an "ISO-8601 + # -like" string, which we must then edit to make compliant), because + # it's been around since git-1.5.3, and it's too difficult to + # discover which version we're using, or to work around using an + # older one. + date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) refnames = keywords["refnames"].strip() if refnames.startswith("$Format"): if verbose: @@ -144,7 +190,7 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose): # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of # just "foo-1.0". If we see a "tag: " prefix, prefer those. TAG = "tag: " - tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)]) + tags = set([r[len(TAG) :] for r in refs if r.startswith(TAG)]) if not tags: # Either we're using git < 1.8.3, or there really are no tags. We use # a heuristic: assume all version tags have a digit. The old git %d @@ -153,54 +199,74 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose): # between branches and tags. By ignoring refnames without digits, we # filter out many common branch names like "release" and # "stabilization", as well as "HEAD" and "master". - tags = set([r for r in refs if re.search(r'\d', r)]) + tags = set([r for r in refs if re.search(r"\d", r)]) if verbose: - print("discarding '%s', no digits" % ",".join(refs-tags)) + print("discarding '%s', no digits" % ",".join(refs - tags)) if verbose: print("likely tags: %s" % ",".join(sorted(tags))) for ref in sorted(tags): # sorting will prefer e.g. "2.0" over "2.0rc1" if ref.startswith(tag_prefix): - r = ref[len(tag_prefix):] + r = ref[len(tag_prefix) :] if verbose: print("picking %s" % r) - return {"version": r, - "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": None - } + return { + "version": r, + "full-revisionid": keywords["full"].strip(), + "dirty": False, + "error": None, + "date": date, + } # no suitable tags, so version is "0+unknown", but full hex is still there if verbose: print("no suitable tags, using unknown + full revision id") - return {"version": "0+unknown", - "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": "no suitable tags"} + return { + "version": "0+unknown", + "full-revisionid": keywords["full"].strip(), + "dirty": False, + "error": "no suitable tags", + "date": None, + } @register_vcs_handler("git", "pieces_from_vcs") def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): - # this runs 'git' from the root of the source tree. This only gets called - # if the git-archive 'subst' keywords were *not* expanded, and - # _version.py hasn't already been rewritten with a short version string, - # meaning we're inside a checked out source tree. - - if not os.path.exists(os.path.join(root, ".git")): - if verbose: - print("no .git in %s" % root) - raise NotThisMethod("no .git directory") + """Get version from 'git describe' in the root of the source tree. + This only gets called if the git-archive 'subst' keywords were *not* + expanded, and _version.py hasn't already been rewritten with a short + version string, meaning we're inside a checked out source tree. + """ GITS = ["git"] if sys.platform == "win32": GITS = ["git.cmd", "git.exe"] - # if there is a tag, this yields TAG-NUM-gHEX[-dirty] - # if there are no tags, this yields HEX[-dirty] (no NUM) - describe_out = run_command(GITS, ["describe", "--tags", "--dirty", - "--always", "--long"], - cwd=root) + + out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root, hide_stderr=True) + if rc != 0: + if verbose: + print("Directory %s not under git control" % root) + raise NotThisMethod("'git rev-parse --git-dir' returned error") + + # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] + # if there isn't one, this yields HEX[-dirty] (no NUM) + describe_out, rc = run_command( + GITS, + [ + "describe", + "--tags", + "--dirty", + "--always", + "--long", + "--match", + "%s*" % tag_prefix, + ], + cwd=root, + ) # --long was added in git-1.5.5 if describe_out is None: raise NotThisMethod("'git describe' failed") describe_out = describe_out.strip() - full_out = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) + full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) if full_out is None: raise NotThisMethod("'git rev-parse' failed") full_out = full_out.strip() @@ -218,17 +284,16 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): dirty = git_describe.endswith("-dirty") pieces["dirty"] = dirty if dirty: - git_describe = git_describe[:git_describe.rindex("-dirty")] + git_describe = git_describe[: git_describe.rindex("-dirty")] # now we have TAG-NUM-gHEX or HEX if "-" in git_describe: # TAG-NUM-gHEX - mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) + mo = re.search(r"^(.+)-(\d+)-g([0-9a-f]+)$", git_describe) if not mo: # unparseable. Maybe git-describe is misbehaving? - pieces["error"] = ("unable to parse git-describe output: '%s'" - % describe_out) + pieces["error"] = "unable to parse git-describe output: '%s'" % describe_out return pieces # tag @@ -237,10 +302,12 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): if verbose: fmt = "tag '%s' doesn't start with prefix '%s'" print(fmt % (full_tag, tag_prefix)) - pieces["error"] = ("tag '%s' doesn't start with prefix '%s'" - % (full_tag, tag_prefix)) + pieces["error"] = "tag '%s' doesn't start with prefix '%s'" % ( + full_tag, + tag_prefix, + ) return pieces - pieces["closest-tag"] = full_tag[len(tag_prefix):] + pieces["closest-tag"] = full_tag[len(tag_prefix) :] # distance: number of commits since tag pieces["distance"] = int(mo.group(2)) @@ -251,27 +318,34 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): else: # HEX: no tags pieces["closest-tag"] = None - count_out = run_command(GITS, ["rev-list", "HEAD", "--count"], - cwd=root) + count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"], cwd=root) pieces["distance"] = int(count_out) # total number of commits + # commit date: see ISO-8601 comment in git_versions_from_keywords() + date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[ + 0 + ].strip() + pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) + return pieces def plus_or_dot(pieces): + """Return a + if we don't already have one, else return a .""" if "+" in pieces.get("closest-tag", ""): return "." return "+" def render_pep440(pieces): - # now build up version string, with post-release "local version - # identifier". Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you - # get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty + """Build up version string, with post-release "local version identifier". - # exceptions: - # 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] + Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you + get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty + Exceptions: + 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] + """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"] or pieces["dirty"]: @@ -281,19 +355,18 @@ def render_pep440(pieces): rendered += ".dirty" else: # exception #1 - rendered = "0+untagged.%d.g%s" % (pieces["distance"], - pieces["short"]) + rendered = "0+untagged.%d.g%s" % (pieces["distance"], pieces["short"]) if pieces["dirty"]: rendered += ".dirty" return rendered def render_pep440_pre(pieces): - # TAG[.post.devDISTANCE] . No -dirty - - # exceptions: - # 1: no tags. 0.post.devDISTANCE + """TAG[.post.devDISTANCE] -- No -dirty. + Exceptions: + 1: no tags. 0.post.devDISTANCE + """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"]: @@ -305,14 +378,15 @@ def render_pep440_pre(pieces): def render_pep440_post(pieces): - # TAG[.postDISTANCE[.dev0]+gHEX] . The ".dev0" means dirty. Note that - # .dev0 sorts backwards (a dirty tree will appear "older" than the - # corresponding clean one), but you shouldn't be releasing software with - # -dirty anyways. + """TAG[.postDISTANCE[.dev0]+gHEX] . - # exceptions: - # 1: no tags. 0.postDISTANCE[.dev0] + The ".dev0" means dirty. Note that .dev0 sorts backwards + (a dirty tree will appear "older" than the corresponding clean one), + but you shouldn't be releasing software with -dirty anyways. + Exceptions: + 1: no tags. 0.postDISTANCE[.dev0] + """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"] or pieces["dirty"]: @@ -331,11 +405,13 @@ def render_pep440_post(pieces): def render_pep440_old(pieces): - # TAG[.postDISTANCE[.dev0]] . The ".dev0" means dirty. + """TAG[.postDISTANCE[.dev0]] . - # exceptions: - # 1: no tags. 0.postDISTANCE[.dev0] + The ".dev0" means dirty. + Eexceptions: + 1: no tags. 0.postDISTANCE[.dev0] + """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"] or pieces["dirty"]: @@ -351,12 +427,13 @@ def render_pep440_old(pieces): def render_git_describe(pieces): - # TAG[-DISTANCE-gHEX][-dirty], like 'git describe --tags --dirty - # --always' + """TAG[-DISTANCE-gHEX][-dirty]. - # exceptions: - # 1: no tags. HEX[-dirty] (note: no 'g' prefix) + Like 'git describe --tags --dirty --always'. + Exceptions: + 1: no tags. HEX[-dirty] (note: no 'g' prefix) + """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"]: @@ -370,12 +447,14 @@ def render_git_describe(pieces): def render_git_describe_long(pieces): - # TAG-DISTANCE-gHEX[-dirty], like 'git describe --tags --dirty - # --always -long'. The distance/hash is unconditional. + """TAG-DISTANCE-gHEX[-dirty]. - # exceptions: - # 1: no tags. HEX[-dirty] (note: no 'g' prefix) + Like 'git describe --tags --dirty --always -long'. + The distance/hash is unconditional. + Exceptions: + 1: no tags. HEX[-dirty] (note: no 'g' prefix) + """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) @@ -388,11 +467,15 @@ def render_git_describe_long(pieces): def render(pieces, style): + """Render the given version pieces into the requested style.""" if pieces["error"]: - return {"version": "unknown", - "full-revisionid": pieces.get("long"), - "dirty": None, - "error": pieces["error"]} + return { + "version": "unknown", + "full-revisionid": pieces.get("long"), + "dirty": None, + "error": pieces["error"], + "date": None, + } if not style or style == "default": style = "pep440" # the default @@ -412,11 +495,17 @@ def render(pieces, style): else: raise ValueError("unknown style '%s'" % style) - return {"version": rendered, "full-revisionid": pieces["long"], - "dirty": pieces["dirty"], "error": None} + return { + "version": rendered, + "full-revisionid": pieces["long"], + "dirty": pieces["dirty"], + "error": None, + "date": pieces.get("date"), + } def get_versions(): + """Get version information or return default if unable to do so.""" # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have # __file__, we can work backwards from there to the root. Some # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which @@ -426,8 +515,7 @@ def get_versions(): verbose = cfg.verbose try: - return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, - verbose) + return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, verbose) except NotThisMethod: pass @@ -436,12 +524,16 @@ def get_versions(): # versionfile_source is the relative path from the top of the source # tree (where the .git directory might live) to this file. Invert # this to find the root from __file__. - for i in cfg.versionfile_source.split('/'): + for i in cfg.versionfile_source.split("/"): root = os.path.dirname(root) except NameError: - return {"version": "0+unknown", "full-revisionid": None, - "dirty": None, - "error": "unable to find root of source tree"} + return { + "version": "0+unknown", + "full-revisionid": None, + "dirty": None, + "error": "unable to find root of source tree", + "date": None, + } try: pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) @@ -455,6 +547,10 @@ def get_versions(): except NotThisMethod: pass - return {"version": "0+unknown", "full-revisionid": None, - "dirty": None, - "error": "unable to compute version"} + return { + "version": "0+unknown", + "full-revisionid": None, + "dirty": None, + "error": "unable to compute version", + "date": None, + } diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..996df50 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,2 @@ +requests +requests-cache diff --git a/requirements-test.txt b/requirements-test.txt new file mode 100644 index 0000000..9955dec --- /dev/null +++ b/requirements-test.txt @@ -0,0 +1,2 @@ +pytest +pytest-cov diff --git a/setup.py b/setup.py index 9c599aa..2d189b2 100644 --- a/setup.py +++ b/setup.py @@ -1,9 +1,7 @@ #!/usr/bin/env python # Standard library modules. -import os -import sys -import logging +from pathlib import Path # Third party modules. from setuptools import setup, find_packages @@ -13,11 +11,10 @@ import versioneer # Globals and constants variables. -logger = logging.getLogger(__name__) -BASEDIR = os.path.abspath(os.path.dirname(__file__)) +BASEDIR = Path(__file__).parent.resolve() -class build_py(_build_py.build_py): +class build_py(_build_py.build_py): def run(self): # Build SQL database import sqlalchemy @@ -26,78 +23,87 @@ def run(self): logging.basicConfig() logger.setLevel(logging.INFO) - filepath = os.path.abspath(os.path.join(BASEDIR, 'pyxray', 'data', 'pyxray.db')) + filepath = os.path.abspath(os.path.join(BASEDIR, "pyxray", "data", "pyxray.db")) if os.path.exists(filepath): os.remove(filepath) - engine = sqlalchemy.create_engine('sqlite:///' + filepath) + engine = sqlalchemy.create_engine("sqlite:///" + filepath) builder = pyxray.sql.build.SqlDatabaseBuilder(engine) builder.build() try: - del self.data_files # Force reinitialization of files to copy + del self.data_files # Force reinitialization of files to copy except AttributeError: pass super().run() -with open(os.path.join(BASEDIR, 'README.rst'), 'r') as fp: + +with open(BASEDIR.joinpath("README.rst"), "r") as fp: LONG_DESCRIPTION = fp.read() -INSTALL_REQUIRES = ['tabulate', 'sqlalchemy', 'tqdm', 'dataclasses;python_version~="3.6"'] -EXTRAS_REQUIRE = {'develop': ['requests', 'requests-cache', 'pytest', 'pytest-cov'] - } +PACKAGES = find_packages() + +PACKAGE_DATA = {"pyxray": ["data/pyxray.db"]} + +with open(BASEDIR.joinpath("requirements.txt"), "r") as fp: + INSTALL_REQUIRES = fp.read().splitlines() + +EXTRAS_REQUIRE = {} +with open(BASEDIR.joinpath("requirements-dev.txt"), "r") as fp: + EXTRAS_REQUIRE["dev"] = fp.read().splitlines() +with open(BASEDIR.joinpath("requirements-test.txt"), "r") as fp: + EXTRAS_REQUIRE["test"] = fp.read().splitlines() CMDCLASS = versioneer.get_cmdclass() -CMDCLASS['build_py'] = build_py +CMDCLASS["build_py"] = build_py ENTRY_POINTS = { - 'pyxray.parser': - [ - 'element symbol = pyxray.parser.notation:ElementSymbolParser', - 'atomic shell notation = pyxray.parser.notation:AtomicShellNotationParser', - 'atomic subshell notation = pyxray.parser.notation:AtomicSubshellNotationParser', - 'generic x-ray transition notation = pyxray.parser.notation:GenericXrayTransitionNotationParser', - 'known x-ray transition notation = pyxray.parser.notation:KnownXrayTransitionNotationParser', - 'series x-ray transition notation = pyxray.parser.notation:SeriesXrayTransitionNotationParser', - 'family x-ray transition notation = pyxray.parser.notation:FamilyXrayTransitionNotationParser', - 'wikipedia element name = pyxray.parser.wikipedia:WikipediaElementNameParser', - 'sargent-welch element atomic weight = pyxray.parser.sargent_welch:SargentWelchElementAtomicWeightParser', - 'sargent-welch element mass density = pyxray.parser.sargent_welch:SargentWelchElementMassDensityParser', - 'perkins1991 = pyxray.parser.perkins1991:Perkins1991Parser', - 'nist atomic weight = pyxray.parser.nist:NISTElementAtomicWeightParser', - 'jeol transition = pyxray.parser.jeol:JEOLTransitionParser', - 'campbell2001 = pyxray.parser.campbell2001:CampbellAtomicSubshellRadiativeWidthParser', - 'dtsa1992 subshell = pyxray.parser.dtsa:DtsaSubshellParser', - 'dtsa1992 transition = pyxray.parser.dtsa:DtsaLineParser', - ], - } - -setup(name="pyxray", - version=versioneer.get_version(), - url='https://github.com/openmicroanalysis/pyxray', - description="Definitions and properties of X-ray transitions", - long_description=LONG_DESCRIPTION, - author="Philippe T. Pinard", - author_email="philippe.pinard@gmail.com", - license="MIT", - classifiers=['Development Status :: 4 - Beta', - 'Intended Audience :: End Users/Desktop', - 'License :: OSI Approved :: MIT License', - 'Natural Language :: English', - 'Programming Language :: Python', - 'Operating System :: OS Independent', - 'Topic :: Scientific/Engineering', - 'Topic :: Scientific/Engineering :: Physics'], - - packages=find_packages(), - package_data={'pyxray': ['data/pyxray.db']}, - - install_requires=INSTALL_REQUIRES, - extras_require=EXTRAS_REQUIRE, - - cmdclass=CMDCLASS, - - entry_points=ENTRY_POINTS, + "pyxray.parser": [ + "element symbol = pyxray.parser.notation:ElementSymbolParser", + "atomic shell notation = pyxray.parser.notation:AtomicShellNotationParser", + "atomic subshell notation = pyxray.parser.notation:AtomicSubshellNotationParser", + "generic x-ray transition notation = pyxray.parser.notation:GenericXrayTransitionNotationParser", + "known x-ray transition notation = pyxray.parser.notation:KnownXrayTransitionNotationParser", + "series x-ray transition notation = pyxray.parser.notation:SeriesXrayTransitionNotationParser", + "family x-ray transition notation = pyxray.parser.notation:FamilyXrayTransitionNotationParser", + "wikipedia element name = pyxray.parser.wikipedia:WikipediaElementNameParser", + "sargent-welch element atomic weight = pyxray.parser.sargent_welch:SargentWelchElementAtomicWeightParser", + "sargent-welch element mass density = pyxray.parser.sargent_welch:SargentWelchElementMassDensityParser", + "perkins1991 = pyxray.parser.perkins1991:Perkins1991Parser", + "nist atomic weight = pyxray.parser.nist:NISTElementAtomicWeightParser", + "jeol transition = pyxray.parser.jeol:JEOLTransitionParser", + "campbell2001 = pyxray.parser.campbell2001:CampbellAtomicSubshellRadiativeWidthParser", + "dtsa1992 subshell = pyxray.parser.dtsa:DtsaSubshellParser", + "dtsa1992 transition = pyxray.parser.dtsa:DtsaLineParser", + ], +} + +setup( + name="pyxray", + version=versioneer.get_version(), + url="https://github.com/openmicroanalysis/pyxray", + author="Philippe T. Pinard", + author_email="philippe.pinard@gmail.com", + classifiers=[ + "Development Status :: 4 - Beta", + "Intended Audience :: End Users/Desktop", + "License :: OSI Approved :: MIT License", + "Natural Language :: English", + "Programming Language :: Python", + "Operating System :: OS Independent", + "Topic :: Scientific/Engineering", + "Topic :: Scientific/Engineering :: Physics", + ], + description="Definitions and properties of X-ray transitions", + long_description=LONG_DESCRIPTION, + long_description_content_type="text/markdown", + license="MIT license", + packages=PACKAGES, + package_data=PACKAGE_DATA, + include_package_data=True, + install_requires=INSTALL_REQUIRES, + extras_require=EXTRAS_REQUIRE, + cmdclass=CMDCLASS, + entry_points=ENTRY_POINTS, ) - diff --git a/versioneer.py b/versioneer.py index c010f63..2b54540 100644 --- a/versioneer.py +++ b/versioneer.py @@ -1,7 +1,7 @@ +# Version: 0.18 -# Version: 0.15 +"""The Versioneer - like a rocketeer, but for versions. -""" The Versioneer ============== @@ -9,7 +9,7 @@ * https://github.com/warner/python-versioneer * Brian Warner * License: Public Domain -* Compatible With: python2.6, 2.7, 3.2, 3.3, 3.4, and pypy +* Compatible With: python2.6, 2.7, 3.2, 3.3, 3.4, 3.5, 3.6, and pypy * [![Latest Version] (https://pypip.in/version/versioneer/badge.svg?style=flat) ](https://pypi.python.org/pypi/versioneer/) @@ -87,125 +87,7 @@ ## Installation -First, decide on values for the following configuration variables: - -* `VCS`: the version control system you use. Currently accepts "git". - -* `style`: the style of version string to be produced. See "Styles" below for - details. Defaults to "pep440", which looks like - `TAG[+DISTANCE.gSHORTHASH[.dirty]]`. - -* `versionfile_source`: - - A project-relative pathname into which the generated version strings should - be written. This is usually a `_version.py` next to your project's main - `__init__.py` file, so it can be imported at runtime. If your project uses - `src/myproject/__init__.py`, this should be `src/myproject/_version.py`. - This file should be checked in to your VCS as usual: the copy created below - by `setup.py setup_versioneer` will include code that parses expanded VCS - keywords in generated tarballs. The 'build' and 'sdist' commands will - replace it with a copy that has just the calculated version string. - - This must be set even if your project does not have any modules (and will - therefore never import `_version.py`), since "setup.py sdist" -based trees - still need somewhere to record the pre-calculated version strings. Anywhere - in the source tree should do. If there is a `__init__.py` next to your - `_version.py`, the `setup.py setup_versioneer` command (described below) - will append some `__version__`-setting assignments, if they aren't already - present. - -* `versionfile_build`: - - Like `versionfile_source`, but relative to the build directory instead of - the source directory. These will differ when your setup.py uses - 'package_dir='. If you have `package_dir={'myproject': 'src/myproject'}`, - then you will probably have `versionfile_build='myproject/_version.py'` and - `versionfile_source='src/myproject/_version.py'`. - - If this is set to None, then `setup.py build` will not attempt to rewrite - any `_version.py` in the built tree. If your project does not have any - libraries (e.g. if it only builds a script), then you should use - `versionfile_build = None` and override `distutils.command.build_scripts` - to explicitly insert a copy of `versioneer.get_version()` into your - generated script. - -* `tag_prefix`: - - a string, like 'PROJECTNAME-', which appears at the start of all VCS tags. - If your tags look like 'myproject-1.2.0', then you should use - tag_prefix='myproject-'. If you use unprefixed tags like '1.2.0', this - should be an empty string. - -* `parentdir_prefix`: - - a optional string, frequently the same as tag_prefix, which appears at the - start of all unpacked tarball filenames. If your tarball unpacks into - 'myproject-1.2.0', this should be 'myproject-'. To disable this feature, - just omit the field from your `setup.cfg`. - -This tool provides one script, named `versioneer`. That script has one mode, -"install", which writes a copy of `versioneer.py` into the current directory -and runs `versioneer.py setup` to finish the installation. - -To versioneer-enable your project: - -* 1: Modify your `setup.cfg`, adding a section named `[versioneer]` and - populating it with the configuration values you decided earlier (note that - the option names are not case-sensitive): - - ```` - [versioneer] - VCS = git - style = pep440 - versionfile_source = src/myproject/_version.py - versionfile_build = myproject/_version.py - tag_prefix = "" - parentdir_prefix = myproject- - ```` - -* 2: Run `versioneer install`. This will do the following: - - * copy `versioneer.py` into the top of your source tree - * create `_version.py` in the right place (`versionfile_source`) - * modify your `__init__.py` (if one exists next to `_version.py`) to define - `__version__` (by calling a function from `_version.py`) - * modify your `MANIFEST.in` to include both `versioneer.py` and the - generated `_version.py` in sdist tarballs - - `versioneer install` will complain about any problems it finds with your - `setup.py` or `setup.cfg`. Run it multiple times until you have fixed all - the problems. - -* 3: add a `import versioneer` to your setup.py, and add the following - arguments to the setup() call: - - version=versioneer.get_version(), - cmdclass=versioneer.get_cmdclass(), - -* 4: commit these changes to your VCS. To make sure you won't forget, - `versioneer install` will mark everything it touched for addition using - `git add`. Don't forget to add `setup.py` and `setup.cfg` too. - -## Post-Installation Usage - -Once established, all uses of your tree from a VCS checkout should get the -current version string. All generated tarballs should include an embedded -version string (so users who unpack them will not need a VCS tool installed). - -If you distribute your project through PyPI, then the release process should -boil down to two steps: - -* 1: git tag 1.0 -* 2: python setup.py register sdist upload - -If you distribute it through github (i.e. users use github to generate -tarballs with `git archive`), the process is: - -* 1: git tag 1.0 -* 2: git push; git push --tags - -Versioneer will report "0+untagged.NUMCOMMITS.gHASH" until your tree has at -least one tag in its history. +See [INSTALL.md](./INSTALL.md) for detailed installation instructions. ## Version-String Flavors @@ -226,6 +108,10 @@ * `['full-revisionid']`: detailed revision identifier. For Git, this is the full SHA1 commit id, e.g. "1076c978a8d3cfc70f408fe5974aa6c092c949ac". +* `['date']`: Date and time of the latest `HEAD` commit. For Git, it is the + commit date in ISO 8601 format. This will be None if the date is not + available. + * `['dirty']`: a boolean, True if the tree has uncommitted changes. Note that this is only accurate if run in a VCS checkout, otherwise it is likely to be False or None @@ -264,8 +150,8 @@ software (exactly equal to a known tag), the identifier will only contain the stripped tag, e.g. "0.11". -Other styles are available. See details.md in the Versioneer source tree for -descriptions. +Other styles are available. See [details.md](details.md) in the Versioneer +source tree for descriptions. ## Debugging @@ -275,47 +161,95 @@ display the full contents of `get_versions()` (including the `error` string, which may help identify what went wrong). -## Updating Versioneer +## Known Limitations -To upgrade your project to a new release of Versioneer, do the following: +Some situations are known to cause problems for Versioneer. This details the +most significant ones. More can be found on Github +[issues page](https://github.com/warner/python-versioneer/issues). -* install the new Versioneer (`pip install -U versioneer` or equivalent) -* edit `setup.cfg`, if necessary, to include any new configuration settings - indicated by the release notes -* re-run `versioneer install` in your source tree, to replace - `SRC/_version.py` -* commit any changed files +### Subprojects + +Versioneer has limited support for source trees in which `setup.py` is not in +the root directory (e.g. `setup.py` and `.git/` are *not* siblings). The are +two common reasons why `setup.py` might not be in the root: + +* Source trees which contain multiple subprojects, such as + [Buildbot](https://github.com/buildbot/buildbot), which contains both + "master" and "slave" subprojects, each with their own `setup.py`, + `setup.cfg`, and `tox.ini`. Projects like these produce multiple PyPI + distributions (and upload multiple independently-installable tarballs). +* Source trees whose main purpose is to contain a C library, but which also + provide bindings to Python (and perhaps other langauges) in subdirectories. + +Versioneer will look for `.git` in parent directories, and most operations +should get the right version string. However `pip` and `setuptools` have bugs +and implementation details which frequently cause `pip install .` from a +subproject directory to fail to find a correct version string (so it usually +defaults to `0+unknown`). + +`pip install --editable .` should work correctly. `setup.py install` might +work too. -### Upgrading to 0.15 +Pip-8.1.1 is known to have this problem, but hopefully it will get fixed in +some later version. -Starting with this version, Versioneer is configured with a `[versioneer]` -section in your `setup.cfg` file. Earlier versions required the `setup.py` to -set attributes on the `versioneer` module immediately after import. The new -version will refuse to run (raising an exception during import) until you -have provided the necessary `setup.cfg` section. +[Bug #38](https://github.com/warner/python-versioneer/issues/38) is tracking +this issue. The discussion in +[PR #61](https://github.com/warner/python-versioneer/pull/61) describes the +issue from the Versioneer side in more detail. +[pip PR#3176](https://github.com/pypa/pip/pull/3176) and +[pip PR#3615](https://github.com/pypa/pip/pull/3615) contain work to improve +pip to let Versioneer work correctly. -In addition, the Versioneer package provides an executable named -`versioneer`, and the installation process is driven by running `versioneer -install`. In 0.14 and earlier, the executable was named -`versioneer-installer` and was run without an argument. +Versioneer-0.16 and earlier only looked for a `.git` directory next to the +`setup.cfg`, so subprojects were completely unsupported with those releases. -### Upgrading to 0.14 +### Editable installs with setuptools <= 18.5 -0.14 changes the format of the version string. 0.13 and earlier used -hyphen-separated strings like "0.11-2-g1076c97-dirty". 0.14 and beyond use a -plus-separated "local version" section strings, with dot-separated -components, like "0.11+2.g1076c97". PEP440-strict tools did not like the old -format, but should be ok with the new one. +`setup.py develop` and `pip install --editable .` allow you to install a +project into a virtualenv once, then continue editing the source code (and +test) without re-installing after every change. -### Upgrading from 0.11 to 0.12 +"Entry-point scripts" (`setup(entry_points={"console_scripts": ..})`) are a +convenient way to specify executable scripts that should be installed along +with the python package. -Nothing special. +These both work as expected when using modern setuptools. When using +setuptools-18.5 or earlier, however, certain operations will cause +`pkg_resources.DistributionNotFound` errors when running the entrypoint +script, which must be resolved by re-installing the package. This happens +when the install happens with one version, then the egg_info data is +regenerated while a different version is checked out. Many setup.py commands +cause egg_info to be rebuilt (including `sdist`, `wheel`, and installing into +a different virtualenv), so this can be surprising. -### Upgrading from 0.10 to 0.11 +[Bug #83](https://github.com/warner/python-versioneer/issues/83) describes +this one, but upgrading to a newer version of setuptools should probably +resolve it. -You must add a `versioneer.VCS = "git"` to your `setup.py` before re-running -`setup.py setup_versioneer`. This will enable the use of additional -version-control systems (SVN, etc) in the future. +### Unicode version strings + +While Versioneer works (and is continually tested) with both Python 2 and +Python 3, it is not entirely consistent with bytes-vs-unicode distinctions. +Newer releases probably generate unicode version strings on py2. It's not +clear that this is wrong, but it may be surprising for applications when then +write these strings to a network connection or include them in bytes-oriented +APIs like cryptographic checksums. + +[Bug #71](https://github.com/warner/python-versioneer/issues/71) investigates +this question. + + +## Updating Versioneer + +To upgrade your project to a new release of Versioneer, do the following: + +* install the new Versioneer (`pip install -U versioneer` or equivalent) +* edit `setup.cfg`, if necessary, to include any new configuration settings + indicated by the release notes. See [UPGRADING](./UPGRADING.md) for details. +* re-run `versioneer install` in your source tree, to replace + `SRC/_version.py` +* commit any changed files ## Future Directions @@ -333,13 +267,16 @@ ## License -To make Versioneer easier to embed, all its code is hereby released into the -public domain. The `_version.py` that it creates is also in the public -domain. +To make Versioneer easier to embed, all its code is dedicated to the public +domain. The `_version.py` that it creates is also in the public domain. +Specifically, both are released under the Creative Commons "Public Domain +Dedication" license (CC0-1.0), as described in +https://creativecommons.org/publicdomain/zero/1.0/ . """ from __future__ import print_function + try: import configparser except ImportError: @@ -353,12 +290,15 @@ class VersioneerConfig: - pass + """Container for Versioneer configuration parameters.""" def get_root(): - # we require that all commands are run from the project root, i.e. the - # directory that contains setup.py, setup.cfg, and versioneer.py . + """Get the project root directory. + + We require that all commands are run from the project root, i.e. the + directory that contains setup.py, setup.cfg, and versioneer.py . + """ root = os.path.realpath(os.path.abspath(os.getcwd())) setup_py = os.path.join(root, "setup.py") versioneer_py = os.path.join(root, "versioneer.py") @@ -368,11 +308,13 @@ def get_root(): setup_py = os.path.join(root, "setup.py") versioneer_py = os.path.join(root, "versioneer.py") if not (os.path.exists(setup_py) or os.path.exists(versioneer_py)): - err = ("Versioneer was unable to run the project root directory. " - "Versioneer requires setup.py to be executed from " - "its immediate directory (like 'python setup.py COMMAND'), " - "or in a way that lets it use sys.argv[0] to find the root " - "(like 'python path/to/setup.py COMMAND').") + err = ( + "Versioneer was unable to run the project root directory. " + "Versioneer requires setup.py to be executed from " + "its immediate directory (like 'python setup.py COMMAND'), " + "or in a way that lets it use sys.argv[0] to find the root " + "(like 'python path/to/setup.py COMMAND')." + ) raise VersioneerBadRootError(err) try: # Certain runtime workflows (setup.py install/develop in a setuptools @@ -382,15 +324,20 @@ def get_root(): # os.path.dirname(__file__), as that will find whichever # versioneer.py was first imported, even in later projects. me = os.path.realpath(os.path.abspath(__file__)) - if os.path.splitext(me)[0] != os.path.splitext(versioneer_py)[0]: - print("Warning: build in %s is using versioneer.py from %s" - % (os.path.dirname(me), versioneer_py)) + me_dir = os.path.normcase(os.path.splitext(me)[0]) + vsr_dir = os.path.normcase(os.path.splitext(versioneer_py)[0]) + if me_dir != vsr_dir: + print( + "Warning: build in %s is using versioneer.py from %s" + % (os.path.dirname(me), versioneer_py) + ) except NameError: pass return root def get_config_from_root(root): + """Read the project setup.cfg file to determine Versioneer config.""" # This might raise EnvironmentError (if setup.cfg is missing), or # configparser.NoSectionError (if it lacks a [versioneer] section), or # configparser.NoOptionError (if it lacks "VCS="). See the docstring at @@ -405,19 +352,23 @@ def get(parser, name): if parser.has_option("versioneer", name): return parser.get("versioneer", name) return None + cfg = VersioneerConfig() cfg.VCS = VCS cfg.style = get(parser, "style") or "" cfg.versionfile_source = get(parser, "versionfile_source") cfg.versionfile_build = get(parser, "versionfile_build") cfg.tag_prefix = get(parser, "tag_prefix") + if cfg.tag_prefix in ("''", '""'): + cfg.tag_prefix = "" cfg.parentdir_prefix = get(parser, "parentdir_prefix") cfg.verbose = get(parser, "verbose") return cfg class NotThisMethod(Exception): - pass + """Exception raised if a method is not valid for the current scenario.""" + # these dictionaries contain VCS-specific tools LONG_VERSION_PY = {} @@ -425,24 +376,33 @@ class NotThisMethod(Exception): def register_vcs_handler(vcs, method): # decorator + """Decorator to mark a method as the handler for a particular VCS.""" + def decorate(f): + """Store f in HANDLERS[vcs][method].""" if vcs not in HANDLERS: HANDLERS[vcs] = {} HANDLERS[vcs][method] = f return f + return decorate -def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False): +def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, env=None): + """Call the given command(s).""" assert isinstance(commands, list) p = None for c in commands: try: dispcmd = str([c] + args) # remember shell=False, so use git.cmd on windows, not just git - p = subprocess.Popen([c] + args, cwd=cwd, stdout=subprocess.PIPE, - stderr=(subprocess.PIPE if hide_stderr - else None)) + p = subprocess.Popen( + [c] + args, + cwd=cwd, + env=env, + stdout=subprocess.PIPE, + stderr=(subprocess.PIPE if hide_stderr else None), + ) break except EnvironmentError: e = sys.exc_info()[1] @@ -451,20 +411,25 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False): if verbose: print("unable to run %s" % dispcmd) print(e) - return None + return None, None else: if verbose: print("unable to find command, tried %s" % (commands,)) - return None + return None, None stdout = p.communicate()[0].strip() if sys.version_info[0] >= 3: stdout = stdout.decode() if p.returncode != 0: if verbose: print("unable to run %s (error)" % dispcmd) - return None - return stdout -LONG_VERSION_PY['git'] = ''' + print("stdout was %s" % stdout) + return None, p.returncode + return stdout, p.returncode + + +LONG_VERSION_PY[ + "git" +] = ''' # This file helps to compute a version number in source trees obtained from # git-archive tarball (such as those provided by githubs download-from-tag # feature). Distribution tarballs (built by setup.py sdist) and build @@ -472,7 +437,9 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False): # that just contains the computed version number. # This file is released into the public domain. Generated by -# versioneer-0.15 (https://github.com/warner/python-versioneer) +# versioneer-0.18 (https://github.com/warner/python-versioneer) + +"""Git implementation of _version.py.""" import errno import os @@ -482,21 +449,24 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False): def get_keywords(): + """Get the keywords needed to look up the version information.""" # these strings will be replaced by git during git-archive. # setup.py/versioneer.py will grep for the variable names, so they must # each be defined on a line of their own. _version.py will just call # get_keywords(). git_refnames = "%(DOLLAR)sFormat:%%d%(DOLLAR)s" git_full = "%(DOLLAR)sFormat:%%H%(DOLLAR)s" - keywords = {"refnames": git_refnames, "full": git_full} + git_date = "%(DOLLAR)sFormat:%%ci%(DOLLAR)s" + keywords = {"refnames": git_refnames, "full": git_full, "date": git_date} return keywords class VersioneerConfig: - pass + """Container for Versioneer configuration parameters.""" def get_config(): + """Create, populate and return the VersioneerConfig() object.""" # these strings are filled in when 'setup.py versioneer' creates # _version.py cfg = VersioneerConfig() @@ -510,7 +480,7 @@ def get_config(): class NotThisMethod(Exception): - pass + """Exception raised if a method is not valid for the current scenario.""" LONG_VERSION_PY = {} @@ -518,7 +488,9 @@ class NotThisMethod(Exception): def register_vcs_handler(vcs, method): # decorator + """Decorator to mark a method as the handler for a particular VCS.""" def decorate(f): + """Store f in HANDLERS[vcs][method].""" if vcs not in HANDLERS: HANDLERS[vcs] = {} HANDLERS[vcs][method] = f @@ -526,14 +498,17 @@ def decorate(f): return decorate -def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False): +def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, + env=None): + """Call the given command(s).""" assert isinstance(commands, list) p = None for c in commands: try: dispcmd = str([c] + args) # remember shell=False, so use git.cmd on windows, not just git - p = subprocess.Popen([c] + args, cwd=cwd, stdout=subprocess.PIPE, + p = subprocess.Popen([c] + args, cwd=cwd, env=env, + stdout=subprocess.PIPE, stderr=(subprocess.PIPE if hide_stderr else None)) break @@ -544,37 +519,50 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False): if verbose: print("unable to run %%s" %% dispcmd) print(e) - return None + return None, None else: if verbose: print("unable to find command, tried %%s" %% (commands,)) - return None + return None, None stdout = p.communicate()[0].strip() if sys.version_info[0] >= 3: stdout = stdout.decode() if p.returncode != 0: if verbose: print("unable to run %%s (error)" %% dispcmd) - return None - return stdout + print("stdout was %%s" %% stdout) + return None, p.returncode + return stdout, p.returncode def versions_from_parentdir(parentdir_prefix, root, verbose): - # Source tarballs conventionally unpack into a directory that includes - # both the project name and a version string. - dirname = os.path.basename(root) - if not dirname.startswith(parentdir_prefix): - if verbose: - print("guessing rootdir is '%%s', but '%%s' doesn't start with " - "prefix '%%s'" %% (root, dirname, parentdir_prefix)) - raise NotThisMethod("rootdir doesn't start with parentdir_prefix") - return {"version": dirname[len(parentdir_prefix):], - "full-revisionid": None, - "dirty": False, "error": None} + """Try to determine the version from the parent directory name. + + Source tarballs conventionally unpack into a directory that includes both + the project name and a version string. We will also support searching up + two directory levels for an appropriately named parent directory + """ + rootdirs = [] + + for i in range(3): + dirname = os.path.basename(root) + if dirname.startswith(parentdir_prefix): + return {"version": dirname[len(parentdir_prefix):], + "full-revisionid": None, + "dirty": False, "error": None, "date": None} + else: + rootdirs.append(root) + root = os.path.dirname(root) # up a level + + if verbose: + print("Tried directories %%s but none started with prefix %%s" %% + (str(rootdirs), parentdir_prefix)) + raise NotThisMethod("rootdir doesn't start with parentdir_prefix") @register_vcs_handler("git", "get_keywords") def git_get_keywords(versionfile_abs): + """Extract version information from the given file.""" # the code embedded in _version.py can just fetch the value of these # keywords. When used from setup.py, we don't want to import _version.py, # so we do it with a regexp instead. This function is not used from @@ -591,6 +579,10 @@ def git_get_keywords(versionfile_abs): mo = re.search(r'=\s*"(.*)"', line) if mo: keywords["full"] = mo.group(1) + if line.strip().startswith("git_date ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["date"] = mo.group(1) f.close() except EnvironmentError: pass @@ -599,8 +591,18 @@ def git_get_keywords(versionfile_abs): @register_vcs_handler("git", "keywords") def git_versions_from_keywords(keywords, tag_prefix, verbose): + """Get version information from git keywords.""" if not keywords: raise NotThisMethod("no keywords at all, weird") + date = keywords.get("date") + if date is not None: + # git-2.2.0 added "%%cI", which expands to an ISO-8601 -compliant + # datestamp. However we prefer "%%ci" (which expands to an "ISO-8601 + # -like" string, which we must then edit to make compliant), because + # it's been around since git-1.5.3, and it's too difficult to + # discover which version we're using, or to work around using an + # older one. + date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) refnames = keywords["refnames"].strip() if refnames.startswith("$Format"): if verbose: @@ -621,7 +623,7 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose): # "stabilization", as well as "HEAD" and "master". tags = set([r for r in refs if re.search(r'\d', r)]) if verbose: - print("discarding '%%s', no digits" %% ",".join(refs-tags)) + print("discarding '%%s', no digits" %% ",".join(refs - tags)) if verbose: print("likely tags: %%s" %% ",".join(sorted(tags))) for ref in sorted(tags): @@ -632,41 +634,46 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose): print("picking %%s" %% r) return {"version": r, "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": None - } + "dirty": False, "error": None, + "date": date} # no suitable tags, so version is "0+unknown", but full hex is still there if verbose: print("no suitable tags, using unknown + full revision id") return {"version": "0+unknown", "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": "no suitable tags"} + "dirty": False, "error": "no suitable tags", "date": None} @register_vcs_handler("git", "pieces_from_vcs") def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): - # this runs 'git' from the root of the source tree. This only gets called - # if the git-archive 'subst' keywords were *not* expanded, and - # _version.py hasn't already been rewritten with a short version string, - # meaning we're inside a checked out source tree. - - if not os.path.exists(os.path.join(root, ".git")): - if verbose: - print("no .git in %%s" %% root) - raise NotThisMethod("no .git directory") + """Get version from 'git describe' in the root of the source tree. + This only gets called if the git-archive 'subst' keywords were *not* + expanded, and _version.py hasn't already been rewritten with a short + version string, meaning we're inside a checked out source tree. + """ GITS = ["git"] if sys.platform == "win32": GITS = ["git.cmd", "git.exe"] - # if there is a tag, this yields TAG-NUM-gHEX[-dirty] - # if there are no tags, this yields HEX[-dirty] (no NUM) - describe_out = run_command(GITS, ["describe", "--tags", "--dirty", - "--always", "--long"], - cwd=root) + + out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root, + hide_stderr=True) + if rc != 0: + if verbose: + print("Directory %%s not under git control" %% root) + raise NotThisMethod("'git rev-parse --git-dir' returned error") + + # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] + # if there isn't one, this yields HEX[-dirty] (no NUM) + describe_out, rc = run_command(GITS, ["describe", "--tags", "--dirty", + "--always", "--long", + "--match", "%%s*" %% tag_prefix], + cwd=root) # --long was added in git-1.5.5 if describe_out is None: raise NotThisMethod("'git describe' failed") describe_out = describe_out.strip() - full_out = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) + full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) if full_out is None: raise NotThisMethod("'git rev-parse' failed") full_out = full_out.strip() @@ -717,27 +724,34 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): else: # HEX: no tags pieces["closest-tag"] = None - count_out = run_command(GITS, ["rev-list", "HEAD", "--count"], - cwd=root) + count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"], + cwd=root) pieces["distance"] = int(count_out) # total number of commits + # commit date: see ISO-8601 comment in git_versions_from_keywords() + date = run_command(GITS, ["show", "-s", "--format=%%ci", "HEAD"], + cwd=root)[0].strip() + pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) + return pieces def plus_or_dot(pieces): + """Return a + if we don't already have one, else return a .""" if "+" in pieces.get("closest-tag", ""): return "." return "+" def render_pep440(pieces): - # now build up version string, with post-release "local version - # identifier". Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you - # get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty + """Build up version string, with post-release "local version identifier". - # exceptions: - # 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] + Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you + get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty + Exceptions: + 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] + """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"] or pieces["dirty"]: @@ -755,11 +769,11 @@ def render_pep440(pieces): def render_pep440_pre(pieces): - # TAG[.post.devDISTANCE] . No -dirty - - # exceptions: - # 1: no tags. 0.post.devDISTANCE + """TAG[.post.devDISTANCE] -- No -dirty. + Exceptions: + 1: no tags. 0.post.devDISTANCE + """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"]: @@ -771,14 +785,15 @@ def render_pep440_pre(pieces): def render_pep440_post(pieces): - # TAG[.postDISTANCE[.dev0]+gHEX] . The ".dev0" means dirty. Note that - # .dev0 sorts backwards (a dirty tree will appear "older" than the - # corresponding clean one), but you shouldn't be releasing software with - # -dirty anyways. + """TAG[.postDISTANCE[.dev0]+gHEX] . - # exceptions: - # 1: no tags. 0.postDISTANCE[.dev0] + The ".dev0" means dirty. Note that .dev0 sorts backwards + (a dirty tree will appear "older" than the corresponding clean one), + but you shouldn't be releasing software with -dirty anyways. + Exceptions: + 1: no tags. 0.postDISTANCE[.dev0] + """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"] or pieces["dirty"]: @@ -797,11 +812,13 @@ def render_pep440_post(pieces): def render_pep440_old(pieces): - # TAG[.postDISTANCE[.dev0]] . The ".dev0" means dirty. + """TAG[.postDISTANCE[.dev0]] . - # exceptions: - # 1: no tags. 0.postDISTANCE[.dev0] + The ".dev0" means dirty. + Eexceptions: + 1: no tags. 0.postDISTANCE[.dev0] + """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"] or pieces["dirty"]: @@ -817,12 +834,13 @@ def render_pep440_old(pieces): def render_git_describe(pieces): - # TAG[-DISTANCE-gHEX][-dirty], like 'git describe --tags --dirty - # --always' + """TAG[-DISTANCE-gHEX][-dirty]. - # exceptions: - # 1: no tags. HEX[-dirty] (note: no 'g' prefix) + Like 'git describe --tags --dirty --always'. + Exceptions: + 1: no tags. HEX[-dirty] (note: no 'g' prefix) + """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"]: @@ -836,12 +854,14 @@ def render_git_describe(pieces): def render_git_describe_long(pieces): - # TAG-DISTANCE-gHEX[-dirty], like 'git describe --tags --dirty - # --always -long'. The distance/hash is unconditional. + """TAG-DISTANCE-gHEX[-dirty]. - # exceptions: - # 1: no tags. HEX[-dirty] (note: no 'g' prefix) + Like 'git describe --tags --dirty --always -long'. + The distance/hash is unconditional. + Exceptions: + 1: no tags. HEX[-dirty] (note: no 'g' prefix) + """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] rendered += "-%%d-g%%s" %% (pieces["distance"], pieces["short"]) @@ -854,11 +874,13 @@ def render_git_describe_long(pieces): def render(pieces, style): + """Render the given version pieces into the requested style.""" if pieces["error"]: return {"version": "unknown", "full-revisionid": pieces.get("long"), "dirty": None, - "error": pieces["error"]} + "error": pieces["error"], + "date": None} if not style or style == "default": style = "pep440" # the default @@ -879,10 +901,12 @@ def render(pieces, style): raise ValueError("unknown style '%%s'" %% style) return {"version": rendered, "full-revisionid": pieces["long"], - "dirty": pieces["dirty"], "error": None} + "dirty": pieces["dirty"], "error": None, + "date": pieces.get("date")} def get_versions(): + """Get version information or return default if unable to do so.""" # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have # __file__, we can work backwards from there to the root. Some # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which @@ -907,7 +931,8 @@ def get_versions(): except NameError: return {"version": "0+unknown", "full-revisionid": None, "dirty": None, - "error": "unable to find root of source tree"} + "error": "unable to find root of source tree", + "date": None} try: pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) @@ -923,12 +948,13 @@ def get_versions(): return {"version": "0+unknown", "full-revisionid": None, "dirty": None, - "error": "unable to compute version"} + "error": "unable to compute version", "date": None} ''' @register_vcs_handler("git", "get_keywords") def git_get_keywords(versionfile_abs): + """Extract version information from the given file.""" # the code embedded in _version.py can just fetch the value of these # keywords. When used from setup.py, we don't want to import _version.py, # so we do it with a regexp instead. This function is not used from @@ -945,6 +971,10 @@ def git_get_keywords(versionfile_abs): mo = re.search(r'=\s*"(.*)"', line) if mo: keywords["full"] = mo.group(1) + if line.strip().startswith("git_date ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["date"] = mo.group(1) f.close() except EnvironmentError: pass @@ -953,8 +983,18 @@ def git_get_keywords(versionfile_abs): @register_vcs_handler("git", "keywords") def git_versions_from_keywords(keywords, tag_prefix, verbose): + """Get version information from git keywords.""" if not keywords: raise NotThisMethod("no keywords at all, weird") + date = keywords.get("date") + if date is not None: + # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant + # datestamp. However we prefer "%ci" (which expands to an "ISO-8601 + # -like" string, which we must then edit to make compliant), because + # it's been around since git-1.5.3, and it's too difficult to + # discover which version we're using, or to work around using an + # older one. + date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) refnames = keywords["refnames"].strip() if refnames.startswith("$Format"): if verbose: @@ -964,7 +1004,7 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose): # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of # just "foo-1.0". If we see a "tag: " prefix, prefer those. TAG = "tag: " - tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)]) + tags = set([r[len(TAG) :] for r in refs if r.startswith(TAG)]) if not tags: # Either we're using git < 1.8.3, or there really are no tags. We use # a heuristic: assume all version tags have a digit. The old git %d @@ -973,54 +1013,74 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose): # between branches and tags. By ignoring refnames without digits, we # filter out many common branch names like "release" and # "stabilization", as well as "HEAD" and "master". - tags = set([r for r in refs if re.search(r'\d', r)]) + tags = set([r for r in refs if re.search(r"\d", r)]) if verbose: - print("discarding '%s', no digits" % ",".join(refs-tags)) + print("discarding '%s', no digits" % ",".join(refs - tags)) if verbose: print("likely tags: %s" % ",".join(sorted(tags))) for ref in sorted(tags): # sorting will prefer e.g. "2.0" over "2.0rc1" if ref.startswith(tag_prefix): - r = ref[len(tag_prefix):] + r = ref[len(tag_prefix) :] if verbose: print("picking %s" % r) - return {"version": r, - "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": None - } + return { + "version": r, + "full-revisionid": keywords["full"].strip(), + "dirty": False, + "error": None, + "date": date, + } # no suitable tags, so version is "0+unknown", but full hex is still there if verbose: print("no suitable tags, using unknown + full revision id") - return {"version": "0+unknown", - "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": "no suitable tags"} + return { + "version": "0+unknown", + "full-revisionid": keywords["full"].strip(), + "dirty": False, + "error": "no suitable tags", + "date": None, + } @register_vcs_handler("git", "pieces_from_vcs") def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): - # this runs 'git' from the root of the source tree. This only gets called - # if the git-archive 'subst' keywords were *not* expanded, and - # _version.py hasn't already been rewritten with a short version string, - # meaning we're inside a checked out source tree. - - if not os.path.exists(os.path.join(root, ".git")): - if verbose: - print("no .git in %s" % root) - raise NotThisMethod("no .git directory") + """Get version from 'git describe' in the root of the source tree. + This only gets called if the git-archive 'subst' keywords were *not* + expanded, and _version.py hasn't already been rewritten with a short + version string, meaning we're inside a checked out source tree. + """ GITS = ["git"] if sys.platform == "win32": GITS = ["git.cmd", "git.exe"] - # if there is a tag, this yields TAG-NUM-gHEX[-dirty] - # if there are no tags, this yields HEX[-dirty] (no NUM) - describe_out = run_command(GITS, ["describe", "--tags", "--dirty", - "--always", "--long"], - cwd=root) + + out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root, hide_stderr=True) + if rc != 0: + if verbose: + print("Directory %s not under git control" % root) + raise NotThisMethod("'git rev-parse --git-dir' returned error") + + # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] + # if there isn't one, this yields HEX[-dirty] (no NUM) + describe_out, rc = run_command( + GITS, + [ + "describe", + "--tags", + "--dirty", + "--always", + "--long", + "--match", + "%s*" % tag_prefix, + ], + cwd=root, + ) # --long was added in git-1.5.5 if describe_out is None: raise NotThisMethod("'git describe' failed") describe_out = describe_out.strip() - full_out = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) + full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) if full_out is None: raise NotThisMethod("'git rev-parse' failed") full_out = full_out.strip() @@ -1038,17 +1098,16 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): dirty = git_describe.endswith("-dirty") pieces["dirty"] = dirty if dirty: - git_describe = git_describe[:git_describe.rindex("-dirty")] + git_describe = git_describe[: git_describe.rindex("-dirty")] # now we have TAG-NUM-gHEX or HEX if "-" in git_describe: # TAG-NUM-gHEX - mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) + mo = re.search(r"^(.+)-(\d+)-g([0-9a-f]+)$", git_describe) if not mo: # unparseable. Maybe git-describe is misbehaving? - pieces["error"] = ("unable to parse git-describe output: '%s'" - % describe_out) + pieces["error"] = "unable to parse git-describe output: '%s'" % describe_out return pieces # tag @@ -1057,10 +1116,12 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): if verbose: fmt = "tag '%s' doesn't start with prefix '%s'" print(fmt % (full_tag, tag_prefix)) - pieces["error"] = ("tag '%s' doesn't start with prefix '%s'" - % (full_tag, tag_prefix)) + pieces["error"] = "tag '%s' doesn't start with prefix '%s'" % ( + full_tag, + tag_prefix, + ) return pieces - pieces["closest-tag"] = full_tag[len(tag_prefix):] + pieces["closest-tag"] = full_tag[len(tag_prefix) :] # distance: number of commits since tag pieces["distance"] = int(mo.group(2)) @@ -1071,14 +1132,24 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): else: # HEX: no tags pieces["closest-tag"] = None - count_out = run_command(GITS, ["rev-list", "HEAD", "--count"], - cwd=root) + count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"], cwd=root) pieces["distance"] = int(count_out) # total number of commits + # commit date: see ISO-8601 comment in git_versions_from_keywords() + date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[ + 0 + ].strip() + pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) + return pieces def do_vcs_install(manifest_in, versionfile_source, ipy): + """Git-specific installation logic for Versioneer. + + For Git, this means creating/changing .gitattributes to mark _version.py + for export-subst keyword substitution. + """ GITS = ["git"] if sys.platform == "win32": GITS = ["git.cmd", "git.exe"] @@ -1112,26 +1183,43 @@ def do_vcs_install(manifest_in, versionfile_source, ipy): def versions_from_parentdir(parentdir_prefix, root, verbose): - # Source tarballs conventionally unpack into a directory that includes - # both the project name and a version string. - dirname = os.path.basename(root) - if not dirname.startswith(parentdir_prefix): - if verbose: - print("guessing rootdir is '%s', but '%s' doesn't start with " - "prefix '%s'" % (root, dirname, parentdir_prefix)) - raise NotThisMethod("rootdir doesn't start with parentdir_prefix") - return {"version": dirname[len(parentdir_prefix):], - "full-revisionid": None, - "dirty": False, "error": None} + """Try to determine the version from the parent directory name. + + Source tarballs conventionally unpack into a directory that includes both + the project name and a version string. We will also support searching up + two directory levels for an appropriately named parent directory + """ + rootdirs = [] + + for i in range(3): + dirname = os.path.basename(root) + if dirname.startswith(parentdir_prefix): + return { + "version": dirname[len(parentdir_prefix) :], + "full-revisionid": None, + "dirty": False, + "error": None, + "date": None, + } + else: + rootdirs.append(root) + root = os.path.dirname(root) # up a level + + if verbose: + print( + "Tried directories %s but none started with prefix %s" + % (str(rootdirs), parentdir_prefix) + ) + raise NotThisMethod("rootdir doesn't start with parentdir_prefix") + SHORT_VERSION_PY = """ -# This file was generated by 'versioneer.py' (0.15) from +# This file was generated by 'versioneer.py' (0.18) from # revision-control system data, or from the parent directory name of an # unpacked source archive. Distribution tarballs contain a pre-generated copy # of this file. import json -import sys version_json = ''' %s @@ -1144,22 +1232,28 @@ def get_versions(): def versions_from_file(filename): + """Try to determine the version from _version.py if present.""" try: with open(filename) as f: contents = f.read() except EnvironmentError: raise NotThisMethod("unable to read _version.py") - mo = re.search(r"version_json = '''\n(.*)''' # END VERSION_JSON", - contents, re.M | re.S) + mo = re.search( + r"version_json = '''\n(.*)''' # END VERSION_JSON", contents, re.M | re.S + ) + if not mo: + mo = re.search( + r"version_json = '''\r\n(.*)''' # END VERSION_JSON", contents, re.M | re.S + ) if not mo: raise NotThisMethod("no version_json in _version.py") return json.loads(mo.group(1)) def write_to_version_file(filename, versions): + """Write the given version number to the given _version.py file.""" os.unlink(filename) - contents = json.dumps(versions, sort_keys=True, - indent=1, separators=(",", ": ")) + contents = json.dumps(versions, sort_keys=True, indent=1, separators=(",", ": ")) with open(filename, "w") as f: f.write(SHORT_VERSION_PY % contents) @@ -1167,19 +1261,21 @@ def write_to_version_file(filename, versions): def plus_or_dot(pieces): + """Return a + if we don't already have one, else return a .""" if "+" in pieces.get("closest-tag", ""): return "." return "+" def render_pep440(pieces): - # now build up version string, with post-release "local version - # identifier". Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you - # get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty + """Build up version string, with post-release "local version identifier". - # exceptions: - # 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] + Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you + get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty + Exceptions: + 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] + """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"] or pieces["dirty"]: @@ -1189,19 +1285,18 @@ def render_pep440(pieces): rendered += ".dirty" else: # exception #1 - rendered = "0+untagged.%d.g%s" % (pieces["distance"], - pieces["short"]) + rendered = "0+untagged.%d.g%s" % (pieces["distance"], pieces["short"]) if pieces["dirty"]: rendered += ".dirty" return rendered def render_pep440_pre(pieces): - # TAG[.post.devDISTANCE] . No -dirty - - # exceptions: - # 1: no tags. 0.post.devDISTANCE + """TAG[.post.devDISTANCE] -- No -dirty. + Exceptions: + 1: no tags. 0.post.devDISTANCE + """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"]: @@ -1213,14 +1308,15 @@ def render_pep440_pre(pieces): def render_pep440_post(pieces): - # TAG[.postDISTANCE[.dev0]+gHEX] . The ".dev0" means dirty. Note that - # .dev0 sorts backwards (a dirty tree will appear "older" than the - # corresponding clean one), but you shouldn't be releasing software with - # -dirty anyways. + """TAG[.postDISTANCE[.dev0]+gHEX] . - # exceptions: - # 1: no tags. 0.postDISTANCE[.dev0] + The ".dev0" means dirty. Note that .dev0 sorts backwards + (a dirty tree will appear "older" than the corresponding clean one), + but you shouldn't be releasing software with -dirty anyways. + Exceptions: + 1: no tags. 0.postDISTANCE[.dev0] + """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"] or pieces["dirty"]: @@ -1239,11 +1335,13 @@ def render_pep440_post(pieces): def render_pep440_old(pieces): - # TAG[.postDISTANCE[.dev0]] . The ".dev0" means dirty. + """TAG[.postDISTANCE[.dev0]] . - # exceptions: - # 1: no tags. 0.postDISTANCE[.dev0] + The ".dev0" means dirty. + Eexceptions: + 1: no tags. 0.postDISTANCE[.dev0] + """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"] or pieces["dirty"]: @@ -1259,12 +1357,13 @@ def render_pep440_old(pieces): def render_git_describe(pieces): - # TAG[-DISTANCE-gHEX][-dirty], like 'git describe --tags --dirty - # --always' + """TAG[-DISTANCE-gHEX][-dirty]. - # exceptions: - # 1: no tags. HEX[-dirty] (note: no 'g' prefix) + Like 'git describe --tags --dirty --always'. + Exceptions: + 1: no tags. HEX[-dirty] (note: no 'g' prefix) + """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"]: @@ -1278,12 +1377,14 @@ def render_git_describe(pieces): def render_git_describe_long(pieces): - # TAG-DISTANCE-gHEX[-dirty], like 'git describe --tags --dirty - # --always -long'. The distance/hash is unconditional. + """TAG-DISTANCE-gHEX[-dirty]. - # exceptions: - # 1: no tags. HEX[-dirty] (note: no 'g' prefix) + Like 'git describe --tags --dirty --always -long'. + The distance/hash is unconditional. + Exceptions: + 1: no tags. HEX[-dirty] (note: no 'g' prefix) + """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) @@ -1296,11 +1397,15 @@ def render_git_describe_long(pieces): def render(pieces, style): + """Render the given version pieces into the requested style.""" if pieces["error"]: - return {"version": "unknown", - "full-revisionid": pieces.get("long"), - "dirty": None, - "error": pieces["error"]} + return { + "version": "unknown", + "full-revisionid": pieces.get("long"), + "dirty": None, + "error": pieces["error"], + "date": None, + } if not style or style == "default": style = "pep440" # the default @@ -1320,17 +1425,24 @@ def render(pieces, style): else: raise ValueError("unknown style '%s'" % style) - return {"version": rendered, "full-revisionid": pieces["long"], - "dirty": pieces["dirty"], "error": None} + return { + "version": rendered, + "full-revisionid": pieces["long"], + "dirty": pieces["dirty"], + "error": None, + "date": pieces.get("date"), + } class VersioneerBadRootError(Exception): - pass + """The project root directory is unknown or missing key files.""" def get_versions(verbose=False): - # returns dict with two keys: 'version' and 'full' + """Get the project version from whatever source is available. + Returns dict with two keys: 'version' and 'full'. + """ if "versioneer" in sys.modules: # see the discussion in cmdclass.py:get_cmdclass() del sys.modules["versioneer"] @@ -1342,8 +1454,9 @@ def get_versions(verbose=False): handlers = HANDLERS.get(cfg.VCS) assert handlers, "unrecognized VCS '%s'" % cfg.VCS verbose = verbose or cfg.verbose - assert cfg.versionfile_source is not None, \ - "please set versioneer.versionfile_source" + assert ( + cfg.versionfile_source is not None + ), "please set versioneer.versionfile_source" assert cfg.tag_prefix is not None, "please set versioneer.tag_prefix" versionfile_abs = os.path.join(root, cfg.versionfile_source) @@ -1397,15 +1510,22 @@ def get_versions(verbose=False): if verbose: print("unable to compute version") - return {"version": "0+unknown", "full-revisionid": None, - "dirty": None, "error": "unable to compute version"} + return { + "version": "0+unknown", + "full-revisionid": None, + "dirty": None, + "error": "unable to compute version", + "date": None, + } def get_version(): + """Get the short version string for this project.""" return get_versions()["version"] def get_cmdclass(): + """Get the custom setuptools/distutils subclasses used by Versioneer.""" if "versioneer" in sys.modules: del sys.modules["versioneer"] # this fixes the "python setup.py develop" case (also 'install' and @@ -1442,8 +1562,10 @@ def run(self): print("Version: %s" % vers["version"]) print(" full-revisionid: %s" % vers.get("full-revisionid")) print(" dirty: %s" % vers.get("dirty")) + print(" date: %s" % vers.get("date")) if vers["error"]: print(" error: %s" % vers["error"]) + cmds["version"] = cmd_version # we override "build_py" in both distutils and setuptools @@ -1455,8 +1577,17 @@ def run(self): # setuptools/bdist_egg -> distutils/install_lib -> build_py # setuptools/install -> bdist_egg ->.. # setuptools/develop -> ? + # pip install: + # copies source tree to a tempdir before running egg_info/etc + # if .git isn't copied too, 'git describe' will fail + # then does setup.py bdist_wheel, or sometimes setup.py install + # setup.py egg_info -> ? - from distutils.command.build_py import build_py as _build_py + # we override different "build_py" commands for both environments + if "setuptools" in sys.modules: + from setuptools.command.build_py import build_py as _build_py + else: + from distutils.command.build_py import build_py as _build_py class cmd_build_py(_build_py): def run(self): @@ -1467,15 +1598,22 @@ def run(self): # now locate _version.py in the new build/ directory and replace # it with an updated value if cfg.versionfile_build: - target_versionfile = os.path.join(self.build_lib, - cfg.versionfile_build) + target_versionfile = os.path.join(self.build_lib, cfg.versionfile_build) print("UPDATING %s" % target_versionfile) write_to_version_file(target_versionfile, versions) + cmds["build_py"] = cmd_build_py if "cx_Freeze" in sys.modules: # cx_freeze enabled? from cx_Freeze.dist import build_exe as _build_exe + # nczeczulin reports that py2exe won't like the pep440-style string + # as FILEVERSION, but it can be used for PRODUCTVERSION, e.g. + # setup(console=[{ + # "version": versioneer.get_version().split("+", 1)[0], # FILEVERSION + # "product_version": versioneer.get_version(), + # ... + class cmd_build_exe(_build_exe): def run(self): root = get_root() @@ -1489,16 +1627,52 @@ def run(self): os.unlink(target_versionfile) with open(cfg.versionfile_source, "w") as f: LONG = LONG_VERSION_PY[cfg.VCS] - f.write(LONG % - {"DOLLAR": "$", - "STYLE": cfg.style, - "TAG_PREFIX": cfg.tag_prefix, - "PARENTDIR_PREFIX": cfg.parentdir_prefix, - "VERSIONFILE_SOURCE": cfg.versionfile_source, - }) + f.write( + LONG + % { + "DOLLAR": "$", + "STYLE": cfg.style, + "TAG_PREFIX": cfg.tag_prefix, + "PARENTDIR_PREFIX": cfg.parentdir_prefix, + "VERSIONFILE_SOURCE": cfg.versionfile_source, + } + ) + cmds["build_exe"] = cmd_build_exe del cmds["build_py"] + if "py2exe" in sys.modules: # py2exe enabled? + try: + from py2exe.distutils_buildexe import py2exe as _py2exe # py3 + except ImportError: + from py2exe.build_exe import py2exe as _py2exe # py2 + + class cmd_py2exe(_py2exe): + def run(self): + root = get_root() + cfg = get_config_from_root(root) + versions = get_versions() + target_versionfile = cfg.versionfile_source + print("UPDATING %s" % target_versionfile) + write_to_version_file(target_versionfile, versions) + + _py2exe.run(self) + os.unlink(target_versionfile) + with open(cfg.versionfile_source, "w") as f: + LONG = LONG_VERSION_PY[cfg.VCS] + f.write( + LONG + % { + "DOLLAR": "$", + "STYLE": cfg.style, + "TAG_PREFIX": cfg.tag_prefix, + "PARENTDIR_PREFIX": cfg.parentdir_prefix, + "VERSIONFILE_SOURCE": cfg.versionfile_source, + } + ) + + cmds["py2exe"] = cmd_py2exe + # we override different "sdist" commands for both environments if "setuptools" in sys.modules: from setuptools.command.sdist import sdist as _sdist @@ -1523,8 +1697,10 @@ def make_release_tree(self, base_dir, files): # updated value target_versionfile = os.path.join(base_dir, cfg.versionfile_source) print("UPDATING %s" % target_versionfile) - write_to_version_file(target_versionfile, - self._versioneer_generated_versions) + write_to_version_file( + target_versionfile, self._versioneer_generated_versions + ) + cmds["sdist"] = cmd_sdist return cmds @@ -1539,7 +1715,7 @@ def make_release_tree(self, base_dir, files): style = pep440 versionfile_source = src/myproject/_version.py versionfile_build = myproject/_version.py - tag_prefix = "" + tag_prefix = parentdir_prefix = myproject- You will also need to edit your setup.py to use the results: @@ -1575,14 +1751,17 @@ def make_release_tree(self, base_dir, files): def do_setup(): + """Main VCS-independent setup function for installing Versioneer.""" root = get_root() try: cfg = get_config_from_root(root) - except (EnvironmentError, configparser.NoSectionError, - configparser.NoOptionError) as e: + except ( + EnvironmentError, + configparser.NoSectionError, + configparser.NoOptionError, + ) as e: if isinstance(e, (EnvironmentError, configparser.NoSectionError)): - print("Adding sample versioneer config to setup.cfg", - file=sys.stderr) + print("Adding sample versioneer config to setup.cfg", file=sys.stderr) with open(os.path.join(root, "setup.cfg"), "a") as f: f.write(SAMPLE_CONFIG) print(CONFIG_ERROR, file=sys.stderr) @@ -1591,15 +1770,18 @@ def do_setup(): print(" creating %s" % cfg.versionfile_source) with open(cfg.versionfile_source, "w") as f: LONG = LONG_VERSION_PY[cfg.VCS] - f.write(LONG % {"DOLLAR": "$", - "STYLE": cfg.style, - "TAG_PREFIX": cfg.tag_prefix, - "PARENTDIR_PREFIX": cfg.parentdir_prefix, - "VERSIONFILE_SOURCE": cfg.versionfile_source, - }) - - ipy = os.path.join(os.path.dirname(cfg.versionfile_source), - "__init__.py") + f.write( + LONG + % { + "DOLLAR": "$", + "STYLE": cfg.style, + "TAG_PREFIX": cfg.tag_prefix, + "PARENTDIR_PREFIX": cfg.parentdir_prefix, + "VERSIONFILE_SOURCE": cfg.versionfile_source, + } + ) + + ipy = os.path.join(os.path.dirname(cfg.versionfile_source), "__init__.py") if os.path.exists(ipy): try: with open(ipy, "r") as f: @@ -1641,21 +1823,24 @@ def do_setup(): else: print(" 'versioneer.py' already in MANIFEST.in") if cfg.versionfile_source not in simple_includes: - print(" appending versionfile_source ('%s') to MANIFEST.in" % - cfg.versionfile_source) + print( + " appending versionfile_source ('%s') to MANIFEST.in" + % cfg.versionfile_source + ) with open(manifest_in, "a") as f: f.write("include %s\n" % cfg.versionfile_source) else: print(" versionfile_source already in MANIFEST.in") # Make VCS-specific changes. For git, this means creating/changing - # .gitattributes to mark _version.py for export-time keyword + # .gitattributes to mark _version.py for export-subst keyword # substitution. do_vcs_install(manifest_in, cfg.versionfile_source, ipy) return 0 def scan_setup_py(): + """Validate the contents of setup.py against Versioneer's expectations.""" found = set() setters = False errors = 0 @@ -1690,6 +1875,7 @@ def scan_setup_py(): errors += 1 return errors + if __name__ == "__main__": cmd = sys.argv[1] if cmd == "setup": From f62e2f42d456abbb96ed4563fab9cce652414ad9 Mon Sep 17 00:00:00 2001 From: Philippe Pinard Date: Sun, 10 May 2020 11:23:51 +0100 Subject: [PATCH 2/8] Fix setup --- setup.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index 2d189b2..3c46074 100644 --- a/setup.py +++ b/setup.py @@ -2,6 +2,7 @@ # Standard library modules. from pathlib import Path +import logging # Third party modules. from setuptools import setup, find_packages @@ -12,6 +13,7 @@ # Globals and constants variables. BASEDIR = Path(__file__).parent.resolve() +logger = logging.getLogger("pyxray") class build_py(_build_py.build_py): @@ -23,11 +25,11 @@ def run(self): logging.basicConfig() logger.setLevel(logging.INFO) - filepath = os.path.abspath(os.path.join(BASEDIR, "pyxray", "data", "pyxray.db")) - if os.path.exists(filepath): - os.remove(filepath) + filepath = BASEDIR.joinpath("pyxray", "data", "pyxray.db").resolve() + if filepath.exists(): + filepath.unlink() - engine = sqlalchemy.create_engine("sqlite:///" + filepath) + engine = sqlalchemy.create_engine("sqlite:///" + str(filepath)) builder = pyxray.sql.build.SqlDatabaseBuilder(engine) builder.build() From f8684a76370164beb44b9f643d56d3f27b00be45 Mon Sep 17 00:00:00 2001 From: Philippe Pinard Date: Sun, 10 May 2020 11:24:21 +0100 Subject: [PATCH 3/8] Apply black formatting --- docs/source/conf.py | 55 +- pyxray/base.py | 145 ++- pyxray/cbook.py | 12 +- pyxray/composition.py | 51 +- pyxray/data.py | 137 ++- pyxray/descriptor.py | 173 ++-- pyxray/parser/base.py | 48 +- pyxray/parser/campbell2001.py | 71 +- pyxray/parser/dtsa.py | 206 ++-- pyxray/parser/jeol.py | 164 +-- pyxray/parser/nist.py | 32 +- pyxray/parser/notation.py | 1505 ++++++++++++++++++---------- pyxray/parser/perkins1991.py | 89 +- pyxray/parser/sargent_welch.py | 251 ++++- pyxray/parser/wikipedia.py | 219 +++- pyxray/property.py | 32 +- pyxray/sql/base.py | 36 +- pyxray/sql/build.py | 18 +- pyxray/sql/data.py | 405 +++++--- pyxray/util.py | 4 +- requirements.txt | 4 +- tests/conftest.py | 3 +- tests/parser/test_campbell2001.py | 1 + tests/parser/test_dtsa.py | 2 + tests/parser/test_jeol.py | 1 + tests/parser/test_nist.py | 1 + tests/parser/test_notation.py | 21 +- tests/parser/test_perkins1991.py | 2 + tests/parser/test_sargent_welch.py | 10 +- tests/parser/test_wikipedia.py | 1 + tests/sql/conftest.py | 73 +- tests/sql/test_build.py | 4 +- tests/sql/test_data.py | 259 +++-- tests/test_base.py | 39 +- tests/test_cbook.py | 7 +- tests/test_composition.py | 61 +- tests/test_descriptor.py | 209 ++-- tests/test_epq.py | 49 +- tests/test_property.py | 17 +- 39 files changed, 2964 insertions(+), 1453 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index cee0ded..7fe4953 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -20,9 +20,9 @@ # -- Project information ----------------------------------------------------- -project = 'pyxray' -copyright = '2019, Philippe T. Pinard' -author = 'Philippe T. Pinard' +project = "pyxray" +copyright = "2019, Philippe T. Pinard" +author = "Philippe T. Pinard" # The short X.Y version version = __version__ @@ -40,24 +40,24 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.doctest', - 'sphinx.ext.mathjax', - 'sphinx.ext.viewcode', - 'sphinx.ext.napoleon', + "sphinx.ext.autodoc", + "sphinx.ext.doctest", + "sphinx.ext.mathjax", + "sphinx.ext.viewcode", + "sphinx.ext.napoleon", ] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] -source_suffix = '.rst' +source_suffix = ".rst" # The master toctree document. -master_doc = 'index' +master_doc = "index" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -69,7 +69,7 @@ # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. -exclude_patterns = ['*/modules.rst'] +exclude_patterns = ["*/modules.rst"] # The name of the Pygments (syntax highlighting) style to use. pygments_style = None @@ -80,7 +80,7 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'sphinx_rtd_theme' +html_theme = "sphinx_rtd_theme" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -91,7 +91,7 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] # Custom sidebar templates, must be a dictionary that maps document names # to template names. @@ -107,7 +107,7 @@ # -- Options for HTMLHelp output --------------------------------------------- # Output file base name for HTML help builder. -htmlhelp_basename = 'pyxraydoc' +htmlhelp_basename = "pyxraydoc" # -- Options for LaTeX output ------------------------------------------------ @@ -116,15 +116,12 @@ # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', - # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', - # Additional stuff for the LaTeX preamble. # # 'preamble': '', - # Latex figure (float) alignment # # 'figure_align': 'htbp', @@ -134,8 +131,7 @@ # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'pyxray.tex', 'pyxray Documentation', - 'Philippe T. Pinard', 'manual'), + (master_doc, "pyxray.tex", "pyxray Documentation", "Philippe T. Pinard", "manual"), ] @@ -143,10 +139,7 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'pyxray', 'pyxray Documentation', - [author], 1) -] +man_pages = [(master_doc, "pyxray", "pyxray Documentation", [author], 1)] # -- Options for Texinfo output ---------------------------------------------- @@ -155,9 +148,15 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'pyxray', 'pyxray Documentation', - author, 'pyxray', 'One line description of project.', - 'Miscellaneous'), + ( + master_doc, + "pyxray", + "pyxray Documentation", + author, + "pyxray", + "One line description of project.", + "Miscellaneous", + ), ] @@ -176,7 +175,7 @@ # epub_uid = '' # A list of files that should not be packed into the epub file. -epub_exclude_files = ['search.html'] +epub_exclude_files = ["search.html"] # -- Extension configuration ------------------------------------------------- diff --git a/pyxray/base.py b/pyxray/base.py index dd68653..2d880e0 100644 --- a/pyxray/base.py +++ b/pyxray/base.py @@ -16,50 +16,45 @@ # Globals and constants variables. + class NotFound(Exception): pass -_docextras = {'element': """:arg element: either + +_docextras = { + "element": """:arg element: either * :class:`Element ` object * atomic number * symbol (case insensitive) * name (in any language, case insensitive) * object with attribute :attr:`atomic_number` or :attr:`z`""", - - 'atomic_shell': """:arg atomic_shell: either + "atomic_shell": """:arg atomic_shell: either * :class:`AtomicShell ` object * principal quantum number * any notation (case insensitive)""", - - 'atomic_subshell': """:arg atomic_subshell: either + "atomic_subshell": """:arg atomic_subshell: either * :class:`AtomicSubshell ` object * a :class:`tuple` of principal quantum number, azimuthal quantum number, and total angular momentum_nominator * any notation (case insensitive)""", - - 'xray_transition': """:arg xray_transition: either + "xray_transition": """:arg xray_transition: either * :class:`XrayTransition ` object * a :class:`tuple` of source and destination subshells * any notation (case insensitive)""", - - 'language': """:arg language: language code (e.g. ``en``, ``fr``, ``de``)""", - - 'notation': """:arg notation: name of a notation (case insensitive), + "language": """:arg language: language code (e.g. ``en``, ``fr``, ``de``)""", + "notation": """:arg notation: name of a notation (case insensitive), ``iupac``, ``siegbahn`` and ``orbital`` are usually supported""", - - 'encoding': """:arg encoding: type of encoding, either + "encoding": """:arg encoding: type of encoding, either ``ascii``, ``utf16``, ``html`` or ``latex``""", - - 'reference': """:arg reference: reference to use to retrieve this value, either + "reference": """:arg reference: reference to use to retrieve this value, either * :class:`Reference ` object * BibTeX key of a reference * ``None``, the default reference will be used or the first reference found""", - - 'exception': """:raise NotFound:""", + "exception": """:raise NotFound:""", } -class _DatabaseMixin(metaclass=abc.ABCMeta): +class _DatabaseMixin(metaclass=abc.ABCMeta): @abc.abstractmethod def get_preferred_references(self): """ @@ -94,7 +89,7 @@ def clear_preferred_references(self): @abc.abstractmethod @formatdoc(**_docextras) - def element(self, element): #pragma: no cover + def element(self, element): # pragma: no cover """ Returns element descriptor. @@ -108,7 +103,7 @@ def element(self, element): #pragma: no cover @abc.abstractmethod @formatdoc(**_docextras) - def element_atomic_number(self, element): #pragma: no cover + def element_atomic_number(self, element): # pragma: no cover """ Returns atomic number of an element. @@ -122,7 +117,7 @@ def element_atomic_number(self, element): #pragma: no cover @abc.abstractmethod @formatdoc(**_docextras) - def element_symbol(self, element, reference=None): #pragma: no cover + def element_symbol(self, element, reference=None): # pragma: no cover """ Returns symbol of an element. @@ -136,7 +131,7 @@ def element_symbol(self, element, reference=None): #pragma: no cover @abc.abstractmethod @formatdoc(**_docextras) - def element_name(self, element, language='en', reference=None): #pragma: no cover + def element_name(self, element, language="en", reference=None): # pragma: no cover """ Returns full name of an element, in the language specified. @@ -152,7 +147,7 @@ def element_name(self, element, language='en', reference=None): #pragma: no cove @abc.abstractmethod @formatdoc(**_docextras) - def element_atomic_weight(self, element, reference=None): #pragma: no cover + def element_atomic_weight(self, element, reference=None): # pragma: no cover """ Returns atomic weight of an element. The atomic weight is defined by the CIAAW as it is the ratio of @@ -170,7 +165,9 @@ def element_atomic_weight(self, element, reference=None): #pragma: no cover @abc.abstractmethod @formatdoc(**_docextras) - def element_mass_density_kg_per_m3(self, element, reference=None): #pragma: no cover + def element_mass_density_kg_per_m3( + self, element, reference=None + ): # pragma: no cover """ Returns mass density (in kg/m3) of an element. @@ -184,7 +181,9 @@ def element_mass_density_kg_per_m3(self, element, reference=None): #pragma: no c raise NotImplementedError @formatdoc(**_docextras) - def element_mass_density_g_per_cm3(self, element, reference=None): #pragma: no cover + def element_mass_density_g_per_cm3( + self, element, reference=None + ): # pragma: no cover """ Returns mass density (in g/cm3) of an element. @@ -199,7 +198,9 @@ def element_mass_density_g_per_cm3(self, element, reference=None): #pragma: no c @abc.abstractmethod @formatdoc(**_docextras) - def element_xray_transition(self, element, xray_transition, reference=None): #pragma: no cover + def element_xray_transition( + self, element, xray_transition, reference=None + ): # pragma: no cover """ Returns X-ray transition descriptor if x-ray transition has a probability greater than 0 for that element. @@ -216,7 +217,9 @@ def element_xray_transition(self, element, xray_transition, reference=None): #pr @abc.abstractmethod @formatdoc(**_docextras) - def element_xray_transitions(self, element, xray_transition=None, reference=None): #pragma: no cover + def element_xray_transitions( + self, element, xray_transition=None, reference=None + ): # pragma: no cover """ Returns all x-ray transitions which have a probability greater than 0 for that element. If *xray_transition* is not ``None``, only the x-ray transitions matching this x-ray transition are returned. @@ -232,7 +235,9 @@ def element_xray_transitions(self, element, xray_transition=None, reference=None raise NotImplementedError @formatdoc(**_docextras) - def print_element_xray_transitions(self, element, file=sys.stdout, tabulate_kwargs=None): + def print_element_xray_transitions( + self, element, file=sys.stdout, tabulate_kwargs=None + ): """ Prints all x-ray transitions for an element, with their different notations and energy. @@ -241,34 +246,36 @@ def print_element_xray_transitions(self, element, file=sys.stdout, tabulate_kwar :arg file: file for output, default to standard out """ - header = ['IUPAC', 'Siegbahn', 'Energy (eV)', 'Probability', 'Relative weight'] + header = ["IUPAC", "Siegbahn", "Energy (eV)", "Probability", "Relative weight"] rows = [] for xray_transition in self.element_xray_transitions(element): try: - iupac = self.xray_transition_notation(xray_transition, 'iupac') + iupac = self.xray_transition_notation(xray_transition, "iupac") except: - iupac = '' + iupac = "" try: - siegbahn = self.xray_transition_notation(xray_transition, 'siegbahn') + siegbahn = self.xray_transition_notation(xray_transition, "siegbahn") except: - siegbahn = '' + siegbahn = "" try: energy_eV = self.xray_transition_energy_eV(element, xray_transition) except: - energy_eV = '' + energy_eV = "" try: probability = self.xray_transition_probability(element, xray_transition) except: - probability = '' + probability = "" try: - relative_weight = self.xray_transition_relative_weight(element, xray_transition) + relative_weight = self.xray_transition_relative_weight( + element, xray_transition + ) except: - relative_weight = '' + relative_weight = "" rows.append([iupac, siegbahn, energy_eV, probability, relative_weight]) @@ -280,7 +287,7 @@ def print_element_xray_transitions(self, element, file=sys.stdout, tabulate_kwar @abc.abstractmethod @formatdoc(**_docextras) - def atomic_shell(self, atomic_shell): #pragma: no cover + def atomic_shell(self, atomic_shell): # pragma: no cover """ Returns atomic shell descriptor. @@ -294,7 +301,9 @@ def atomic_shell(self, atomic_shell): #pragma: no cover @abc.abstractmethod @formatdoc(**_docextras) - def atomic_shell_notation(self, atomic_shell, notation, encoding='utf16', reference=None): #pragma: no cover + def atomic_shell_notation( + self, atomic_shell, notation, encoding="utf16", reference=None + ): # pragma: no cover """ Returns notation of an atomic shell. @@ -311,7 +320,7 @@ def atomic_shell_notation(self, atomic_shell, notation, encoding='utf16', refere @abc.abstractmethod @formatdoc(**_docextras) - def atomic_subshell(self, atomic_subshell): #pragma: no cover + def atomic_subshell(self, atomic_subshell): # pragma: no cover """ Returns atomic subshell descriptor. @@ -325,7 +334,9 @@ def atomic_subshell(self, atomic_subshell): #pragma: no cover @abc.abstractmethod @formatdoc(**_docextras) - def atomic_subshell_notation(self, atomic_subshell, notation, encoding='utf16', reference=None): #pragma: no cover + def atomic_subshell_notation( + self, atomic_subshell, notation, encoding="utf16", reference=None + ): # pragma: no cover """ Returns notation of an atomic subshell. @@ -342,7 +353,9 @@ def atomic_subshell_notation(self, atomic_subshell, notation, encoding='utf16', @abc.abstractmethod @formatdoc(**_docextras) - def atomic_subshell_binding_energy_eV(self, element, atomic_subshell, reference=None): #pragma: no cover + def atomic_subshell_binding_energy_eV( + self, element, atomic_subshell, reference=None + ): # pragma: no cover """ Returns binding energy of an element and atomic subshell (in eV). @@ -358,7 +371,9 @@ def atomic_subshell_binding_energy_eV(self, element, atomic_subshell, reference= @abc.abstractmethod @formatdoc(**_docextras) - def atomic_subshell_radiative_width_eV(self, element, atomic_subshell, reference=None): #pragma: no cover + def atomic_subshell_radiative_width_eV( + self, element, atomic_subshell, reference=None + ): # pragma: no cover """ Returns radiative width of an element and atomic subshell (in eV). @@ -374,7 +389,9 @@ def atomic_subshell_radiative_width_eV(self, element, atomic_subshell, reference @abc.abstractmethod @formatdoc(**_docextras) - def atomic_subshell_nonradiative_width_eV(self, element, atomic_subshell, reference=None): #pragma: no cover + def atomic_subshell_nonradiative_width_eV( + self, element, atomic_subshell, reference=None + ): # pragma: no cover """ Returns nonradiative width of an element and atomic subshell (in eV). @@ -390,7 +407,9 @@ def atomic_subshell_nonradiative_width_eV(self, element, atomic_subshell, refere @abc.abstractmethod @formatdoc(**_docextras) - def atomic_subshell_occupancy(self, element, atomic_subshell, reference=None): #pragma: no cover + def atomic_subshell_occupancy( + self, element, atomic_subshell, reference=None + ): # pragma: no cover """ Returns occupancy of an element and atomic subshell. @@ -406,7 +425,7 @@ def atomic_subshell_occupancy(self, element, atomic_subshell, reference=None): # @abc.abstractmethod @formatdoc(**_docextras) - def xray_transition(self, xray_transition): #pragma: no cover + def xray_transition(self, xray_transition): # pragma: no cover """ Returns x-ray transition descriptor. @@ -420,7 +439,9 @@ def xray_transition(self, xray_transition): #pragma: no cover @abc.abstractmethod @formatdoc(**_docextras) - def xray_transition_notation(self, xray_transition, notation, encoding='utf16', reference=None): #pragma: no cover + def xray_transition_notation( + self, xray_transition, notation, encoding="utf16", reference=None + ): # pragma: no cover """ Returns notation of an x-ray transition. @@ -437,7 +458,9 @@ def xray_transition_notation(self, xray_transition, notation, encoding='utf16', @abc.abstractmethod @formatdoc(**_docextras) - def xray_transition_energy_eV(self, element, xray_transition, reference=None): #pragma: no cover + def xray_transition_energy_eV( + self, element, xray_transition, reference=None + ): # pragma: no cover """ Returns energy of an element and X-ray transition (in eV). @@ -453,7 +476,9 @@ def xray_transition_energy_eV(self, element, xray_transition, reference=None): # @abc.abstractmethod @formatdoc(**_docextras) - def xray_transition_probability(self, element, xray_transition, reference=None): #pragma: no cover + def xray_transition_probability( + self, element, xray_transition, reference=None + ): # pragma: no cover """ Returns probability of an element and X-ray transition. @@ -469,7 +494,9 @@ def xray_transition_probability(self, element, xray_transition, reference=None): @abc.abstractmethod @formatdoc(**_docextras) - def xray_transition_relative_weight(self, element, xray_transition, reference=None): #pragma: no cover + def xray_transition_relative_weight( + self, element, xray_transition, reference=None + ): # pragma: no cover """ Returns relative weight of an element and X-ray transition. @@ -500,10 +527,14 @@ def xray_line(self, element, xray_transition): transition = self.xray_transition(xray_transition) - iupac = '{} {}'.format(symbol, self.xray_transition_notation(transition, 'iupac', 'utf16')) + iupac = "{} {}".format( + symbol, self.xray_transition_notation(transition, "iupac", "utf16") + ) try: - siegbahn = '{} {}'.format(symbol, self.xray_transition_notation(transition, 'siegbahn', 'utf16')) + siegbahn = "{} {}".format( + symbol, self.xray_transition_notation(transition, "siegbahn", "utf16") + ) except: siegbahn = iupac @@ -522,4 +553,12 @@ def xray_line(self, element, xray_transition): except NotFound: relative_weight = None - return descriptor.XrayLine(element, transition, iupac, siegbahn, energy_eV, probability, relative_weight) + return descriptor.XrayLine( + element, + transition, + iupac, + siegbahn, + energy_eV, + probability, + relative_weight, + ) diff --git a/pyxray/cbook.py b/pyxray/cbook.py index 79d7708..6f69691 100644 --- a/pyxray/cbook.py +++ b/pyxray/cbook.py @@ -10,8 +10,8 @@ # Globals and constants variables. -class ProgressMixin: +class ProgressMixin: def update(self, progress): """ Update the progress, a value between 0 and 100. @@ -24,13 +24,13 @@ def progress(self): """ Current progress, a value between 0 and 100. """ - return getattr(self, '_progress', 0) + return getattr(self, "_progress", 0) -class ProgressReportMixin(ProgressMixin): +class ProgressReportMixin(ProgressMixin): def add_reporthook(self, hook): assert callable(hook) - if not hasattr(self, '_reporthooks'): + if not hasattr(self, "_reporthooks"): self._reporthooks = set() self._reporthooks.add(hook) @@ -39,11 +39,13 @@ def clear_reporthooks(self): def update(self, progress): super().update(progress) - for hook in getattr(self, '_reporthooks', []): + for hook in getattr(self, "_reporthooks", []): hook(progress) + def formatdoc(*formatargs, **formatkwargs): def decorate(func): func.__doc__ = func.__doc__.format(*formatargs, **formatkwargs) return func + return decorate diff --git a/pyxray/composition.py b/pyxray/composition.py index 7eb59e6..77ea80e 100644 --- a/pyxray/composition.py +++ b/pyxray/composition.py @@ -1,6 +1,6 @@ """""" -__all__ = ['Composition'] +__all__ = ["Composition"] # Standard library modules. import math @@ -15,7 +15,8 @@ # Local modules. # Globals and constants variables. -CHEMICAL_FORMULA_PATTERN = re.compile(r'([A-Z][a-z]?)([0-9\.]*)') +CHEMICAL_FORMULA_PATTERN = re.compile(r"([A-Z][a-z]?)([0-9\.]*)") + def process_wildcard(fractions): """ @@ -25,7 +26,7 @@ def process_wildcard(fractions): wildcard_zs = set() total_fraction = 0.0 for z, fraction in fractions.items(): - if fraction == '?': + if fraction == "?": wildcard_zs.add(z) else: total_fraction += fraction @@ -39,6 +40,7 @@ def process_wildcard(fractions): return fractions + def convert_mass_to_atomic_fractions(mass_fractions): """ Converts a mass fraction :class:`dict` to an atomic fraction :class:`dict`. @@ -64,6 +66,7 @@ def convert_mass_to_atomic_fractions(mass_fractions): return atomic_fractions + def convert_atomic_to_mass_fractions(atomic_fractions): """ Converts an atomic fraction :class:`dict` to a mass fraction :class:`dict`. @@ -89,6 +92,7 @@ def convert_atomic_to_mass_fractions(atomic_fractions): return mass_fractions + def convert_formula_to_atomic_fractions(formula): """ Converts a chemical formula to an atomic fraction :class:`dict`. @@ -104,7 +108,7 @@ def convert_formula_to_atomic_fractions(formula): z = pyxray.element_atomic_number(symbol.strip()) - if mole_fraction == '': + if mole_fraction == "": mole_fraction = 1.0 mole_fraction = float(mole_fraction) @@ -119,13 +123,14 @@ def convert_formula_to_atomic_fractions(formula): return atomic_fractions + def generate_name(atomic_fractions): """ Generates a name from the composition. The name is generated on the basis of a classical chemical formula. """ if not atomic_fractions: - return '' + return "" if len(atomic_fractions) == 1: z = list(atomic_fractions.keys())[0] @@ -144,7 +149,7 @@ def generate_name(atomic_fractions): smallest_gcd = min(gcds) # Write formula - name = '' + name = "" for symbol, fraction in zip(symbols, fractions): mole_fraction = int(fraction * smallest_gcd) if mole_fraction == 0: @@ -152,10 +157,11 @@ def generate_name(atomic_fractions): elif mole_fraction == 1: name += "%s" % symbol else: - name += '%s%i' % (symbol, mole_fraction) + name += "%s%i" % (symbol, mole_fraction) return name + class Composition: """ Defines a composition of a compound. @@ -180,16 +186,16 @@ class Composition: """ _key = object() - PRECISION = 0.000000001 # 1ppb + PRECISION = 0.000000001 # 1ppb def __init__(self, key, mass_fractions, atomic_fractions, formula): """ Private constructor. It should never be used. """ if key != Composition._key: - raise TypeError('Composition cannot be created using constructor') + raise TypeError("Composition cannot be created using constructor") if set(mass_fractions.keys()) != set(atomic_fractions.keys()): - raise ValueError('Mass and atomic fractions must have the same elements') + raise ValueError("Mass and atomic fractions must have the same elements") self.mass_fractions = MappingProxyType(mass_fractions) self.atomic_fractions = MappingProxyType(atomic_fractions) @@ -264,7 +270,7 @@ def __iter__(self): return iter(self.mass_fractions.keys()) def __repr__(self): - return '<{}({})>'.format(self.__class__.__name__, self.inner_repr()) + return "<{}({})>".format(self.__class__.__name__, self.inner_repr()) def __eq__(self, other): if not isinstance(other, self.__class__): @@ -297,20 +303,27 @@ def __hash__(self): return hash(tuple(out)) def __getstate__(self): - return {'mass_fractions': dict(self.mass_fractions), - 'atomic_fractions': dict(self.atomic_fractions), - 'formula': self.formula} + return { + "mass_fractions": dict(self.mass_fractions), + "atomic_fractions": dict(self.atomic_fractions), + "formula": self.formula, + } def __setstate__(self, state): - self.mass_fractions = MappingProxyType(state.get('mass_fractions', {})) - self.atomic_fractions = MappingProxyType(state.get('atomic_fractions', {})) - self._formula = state.get('formula', '') + self.mass_fractions = MappingProxyType(state.get("mass_fractions", {})) + self.atomic_fractions = MappingProxyType(state.get("atomic_fractions", {})) + self._formula = state.get("formula", "") def is_normalized(self): - return math.isclose(sum(self.mass_fractions.values()), 1.0, abs_tol=self.PRECISION) + return math.isclose( + sum(self.mass_fractions.values()), 1.0, abs_tol=self.PRECISION + ) def inner_repr(self): - return ', '.join('{}: {:.4f}'.format(pyxray.element_symbol(z), mass_fraction) for z, mass_fraction in self.mass_fractions.items()) + return ", ".join( + "{}: {:.4f}".format(pyxray.element_symbol(z), mass_fraction) + for z, mass_fraction in self.mass_fractions.items() + ) @property def formula(self): diff --git a/pyxray/data.py b/pyxray/data.py index 029ee7a..5042c82 100644 --- a/pyxray/data.py +++ b/pyxray/data.py @@ -3,34 +3,34 @@ """ __all__ = [ - 'get_preferred_references', - 'add_preferred_reference', - 'clear_preferred_references', - 'element', - 'element_atomic_number', - 'element_symbol', - 'element_name', - 'element_atomic_weight', - 'element_mass_density_kg_per_m3', - 'element_mass_density_g_per_cm3', - 'element_xray_transitions', - 'element_xray_transition', - 'print_element_xray_transitions', - 'atomic_shell', - 'atomic_shell_notation', - 'atomic_subshell', - 'atomic_subshell_notation', - 'atomic_subshell_binding_energy_eV', - 'atomic_subshell_radiative_width_eV', - 'atomic_subshell_nonradiative_width_eV', - 'atomic_subshell_occupancy', - 'xray_transition', - 'xray_transition_notation', - 'xray_transition_energy_eV', - 'xray_transition_probability', - 'xray_transition_relative_weight', - 'xray_line', - ] + "get_preferred_references", + "add_preferred_reference", + "clear_preferred_references", + "element", + "element_atomic_number", + "element_symbol", + "element_name", + "element_atomic_weight", + "element_mass_density_kg_per_m3", + "element_mass_density_g_per_cm3", + "element_xray_transitions", + "element_xray_transition", + "print_element_xray_transitions", + "atomic_shell", + "atomic_shell_notation", + "atomic_subshell", + "atomic_subshell_notation", + "atomic_subshell_binding_energy_eV", + "atomic_subshell_radiative_width_eV", + "atomic_subshell_nonradiative_width_eV", + "atomic_subshell_occupancy", + "xray_transition", + "xray_transition_notation", + "xray_transition_energy_eV", + "xray_transition_probability", + "xray_transition_relative_weight", + "xray_line", +] # Standard library modules. import os @@ -46,8 +46,8 @@ # Globals and constants variables. logger = logging.getLogger(__name__) -class _EmptyDatabase(_DatabaseMixin): +class _EmptyDatabase(_DatabaseMixin): def get_preferred_references(self): return () @@ -57,85 +57,112 @@ def add_preferred_reference(self, reference): def clear_preferred_references(self): pass - def element(self, element): #pragma: no cover + def element(self, element): # pragma: no cover raise NotFound - def element_atomic_number(self, element): #pragma: no cover + def element_atomic_number(self, element): # pragma: no cover raise NotFound - def element_symbol(self, element, reference=None): #pragma: no cover + def element_symbol(self, element, reference=None): # pragma: no cover raise NotFound - def element_name(self, element, language='en', reference=None): #pragma: no cover + def element_name(self, element, language="en", reference=None): # pragma: no cover raise NotFound - def element_atomic_weight(self, element, reference=None): #pragma: no cover + def element_atomic_weight(self, element, reference=None): # pragma: no cover raise NotFound - def element_mass_density_kg_per_m3(self, element, reference=None): #pragma: no cover + def element_mass_density_kg_per_m3( + self, element, reference=None + ): # pragma: no cover raise NotFound - def element_mass_density_g_per_cm3(self, element, reference=None): #pragma: no cover + def element_mass_density_g_per_cm3( + self, element, reference=None + ): # pragma: no cover raise NotFound - def element_xray_transitions(self, element, xray_transition=None, reference=None): #pragma: no cover + def element_xray_transitions( + self, element, xray_transition=None, reference=None + ): # pragma: no cover raise NotFound def element_xray_transition(self, element, xray_transition, reference=None): raise NotFound - def atomic_shell(self, atomic_shell): #pragma: no cover + def atomic_shell(self, atomic_shell): # pragma: no cover raise NotFound - def atomic_shell_notation(self, atomic_shell, notation, encoding='utf16', reference=None): #pragma: no cover + def atomic_shell_notation( + self, atomic_shell, notation, encoding="utf16", reference=None + ): # pragma: no cover raise NotFound - def atomic_subshell(self, atomic_subshell): #pragma: no cover + def atomic_subshell(self, atomic_subshell): # pragma: no cover raise NotFound - def atomic_subshell_notation(self, atomic_subshell, notation, encoding='utf16', reference=None): #pragma: no cover + def atomic_subshell_notation( + self, atomic_subshell, notation, encoding="utf16", reference=None + ): # pragma: no cover raise NotFound - def atomic_subshell_binding_energy_eV(self, element, atomic_subshell, reference=None): #pragma: no cover + def atomic_subshell_binding_energy_eV( + self, element, atomic_subshell, reference=None + ): # pragma: no cover raise NotFound - def atomic_subshell_radiative_width_eV(self, element, atomic_subshell, reference=None): #pragma: no cover + def atomic_subshell_radiative_width_eV( + self, element, atomic_subshell, reference=None + ): # pragma: no cover raise NotFound - def atomic_subshell_nonradiative_width_eV(self, element, atomic_subshell, reference=None): #pragma: no cover + def atomic_subshell_nonradiative_width_eV( + self, element, atomic_subshell, reference=None + ): # pragma: no cover raise NotFound - def atomic_subshell_occupancy(self, element, atomic_subshell, reference=None): #pragma: no cover + def atomic_subshell_occupancy( + self, element, atomic_subshell, reference=None + ): # pragma: no cover raise NotFound - def xray_transition(self, xraytransition): #pragma: no cover + def xray_transition(self, xraytransition): # pragma: no cover raise NotFound - def xray_transition_notation(self, xray_transition, notation, encoding='utf16', reference=None): #pragma: no cover + def xray_transition_notation( + self, xray_transition, notation, encoding="utf16", reference=None + ): # pragma: no cover raise NotFound - def xray_transition_energy_eV(self, element, xray_transition, reference=None): #pragma: no cover + def xray_transition_energy_eV( + self, element, xray_transition, reference=None + ): # pragma: no cover raise NotFound - def xray_transition_probability(self, element, xray_transition, reference=None): #pragma: no cover + def xray_transition_probability( + self, element, xray_transition, reference=None + ): # pragma: no cover raise NotFound - def xray_transition_relative_weight(self, element, xray_transition, reference=None): #pragma: no cover + def xray_transition_relative_weight( + self, element, xray_transition, reference=None + ): # pragma: no cover raise NotFound - def xray_line(self, element, xray_transition): #pragma: no cover + def xray_line(self, element, xray_transition): # pragma: no cover raise NotFound + def _init_sql_database(): basedir = os.path.abspath(os.path.dirname(__file__)) - filepath = os.path.join(basedir, 'data', 'pyxray.db') + filepath = os.path.join(basedir, "data", "pyxray.db") if not os.path.exists(filepath): - raise RuntimeError('Cannot find SQL database at location {0}' - .format(filepath)) + raise RuntimeError("Cannot find SQL database at location {0}".format(filepath)) - engine = sqlalchemy.create_engine('sqlite:///' + filepath) + engine = sqlalchemy.create_engine("sqlite:///" + filepath) return SqlDatabase(engine) + try: database = _init_sql_database() except: diff --git a/pyxray/descriptor.py b/pyxray/descriptor.py index 93079e6..0362af5 100644 --- a/pyxray/descriptor.py +++ b/pyxray/descriptor.py @@ -2,7 +2,16 @@ Definition of descriptors. """ -__all__ = ['Element', 'AtomicShell', 'AtomicSubshell', 'XrayTransition', 'XrayLine', 'Language', 'Notation', 'Reference'] +__all__ = [ + "Element", + "AtomicShell", + "AtomicSubshell", + "XrayTransition", + "XrayLine", + "Language", + "Notation", + "Reference", +] # Standard library modules. import dataclasses @@ -16,38 +25,45 @@ # Globals and constants variables. + @dataclasses.dataclass(frozen=True, order=True) class Element: atomic_number: int def __post_init__(self): if self.atomic_number < 1 or self.atomic_number > 118: - raise ValueError('Atomic number ({0}) must be [1, 118]' - .format(self.atomic_number)) + raise ValueError( + "Atomic number ({0}) must be [1, 118]".format(self.atomic_number) + ) def __repr__(self): - return '{}(z={})'.format(self.__class__.__name__, self.atomic_number) + return "{}(z={})".format(self.__class__.__name__, self.atomic_number) @property def z(self): return self.atomic_number + @dataclasses.dataclass(frozen=True, order=True) class AtomicShell: principal_quantum_number: int def __post_init__(self): if self.principal_quantum_number < 1: - raise ValueError('Principal quantum number ({0}) must be [1, inf[' - .format(self.principal_quantum_number)) + raise ValueError( + "Principal quantum number ({0}) must be [1, inf[".format( + self.principal_quantum_number + ) + ) def __repr__(self): - return '{}(n={})'.format(self.__class__.__name__, self.principal_quantum_number) + return "{}(n={})".format(self.__class__.__name__, self.principal_quantum_number) @property def n(self): return self.principal_quantum_number + @dataclasses.dataclass(frozen=True, order=True) class AtomicSubshell: principal_quantum_number: int @@ -56,32 +72,50 @@ class AtomicSubshell: def __post_init__(self): if isinstance(self.principal_quantum_number, AtomicShell): - object.__setattr__(self, 'principal_quantum_number', self.principal_quantum_number.n) - - if self.principal_quantum_number is not None and self.azimuthal_quantum_number is not None: + object.__setattr__( + self, "principal_quantum_number", self.principal_quantum_number.n + ) + + if ( + self.principal_quantum_number is not None + and self.azimuthal_quantum_number is not None + ): lmin = 0 lmax = self.principal_quantum_number - 1 - if self.azimuthal_quantum_number < lmin or \ - self.azimuthal_quantum_number > lmax: - raise ValueError('Azimuthal quantum number ({0}) must be between [{1}, {2}]' - .format(self.azimuthal_quantum_number, lmin, lmax)) - - if self.azimuthal_quantum_number is not None and self.total_angular_momentum_nominator is not None: + if ( + self.azimuthal_quantum_number < lmin + or self.azimuthal_quantum_number > lmax + ): + raise ValueError( + "Azimuthal quantum number ({0}) must be between [{1}, {2}]".format( + self.azimuthal_quantum_number, lmin, lmax + ) + ) + + if ( + self.azimuthal_quantum_number is not None + and self.total_angular_momentum_nominator is not None + ): jmin_n = 2 * abs(self.azimuthal_quantum_number - 0.5) jmax_n = 2 * abs(self.azimuthal_quantum_number + 0.5) - if self.total_angular_momentum_nominator < jmin_n or \ - self.total_angular_momentum_nominator > jmax_n: - raise ValueError('Total angular momentum ({0}) must be between [{1}, {2}]' - .format(self.total_angular_momentum_nominator, jmin_n, jmax_n)) + if ( + self.total_angular_momentum_nominator < jmin_n + or self.total_angular_momentum_nominator > jmax_n + ): + raise ValueError( + "Total angular momentum ({0}) must be between [{1}, {2}]".format( + self.total_angular_momentum_nominator, jmin_n, jmax_n + ) + ) def __repr__(self): def _format(value): - return '*' if value is None else value - return '{}(n={}, l={}, j={:.1f})'.format(self.__class__.__name__, - _format(self.n), - _format(self.l), - _format(self.j)) + return "*" if value is None else value + + return "{}(n={}, l={}, j={:.1f})".format( + self.__class__.__name__, _format(self.n), _format(self.l), _format(self.j) + ) @property def atomic_shell(self): @@ -109,6 +143,7 @@ def total_angular_momentum(self): def j(self): return self.total_angular_momentum + @dataclasses.dataclass(frozen=True) class XrayTransition: source_principal_quantum_number: int = None @@ -130,7 +165,7 @@ def __init__(self, *args): src_l = args[0].l src_j_n = args[0].j_n else: - raise ValueError('Unknown argument: {}'.format(args[0])) + raise ValueError("Unknown argument: {}".format(args[0])) if isinstance(args[1], Sequence): dst_n, dst_l, dst_j_n = args[1] @@ -139,7 +174,7 @@ def __init__(self, *args): dst_l = args[1].l dst_j_n = args[1].j_n else: - raise ValueError('Unknown argument: {}'.format(args[1])) + raise ValueError("Unknown argument: {}".format(args[1])) elif len(args) == 4: if isinstance(args[0], Sequence): @@ -160,20 +195,23 @@ def __init__(self, *args): dst_j_n = args[-1].j_n else: - raise ValueError('Unsupported number of arguments: {}'.format(len(args))) + raise ValueError("Unsupported number of arguments: {}".format(len(args))) - object.__setattr__(self, 'source_principal_quantum_number', src_n) - object.__setattr__(self, 'source_azimuthal_quantum_number', src_l) - object.__setattr__(self, 'source_total_angular_momentum_nominator', src_j_n) - object.__setattr__(self, 'destination_principal_quantum_number', dst_n) - object.__setattr__(self, 'destination_azimuthal_quantum_number', dst_l) - object.__setattr__(self, 'destination_total_angular_momentum_nominator', dst_j_n) + object.__setattr__(self, "source_principal_quantum_number", src_n) + object.__setattr__(self, "source_azimuthal_quantum_number", src_l) + object.__setattr__(self, "source_total_angular_momentum_nominator", src_j_n) + object.__setattr__(self, "destination_principal_quantum_number", dst_n) + object.__setattr__(self, "destination_azimuthal_quantum_number", dst_l) + object.__setattr__( + self, "destination_total_angular_momentum_nominator", dst_j_n + ) @classmethod def is_radiative(cls, source_subshell, destination_subshell): """ Inspired from NIST EPQ library by Nicholas Ritchie. """ + def electric_dipole_permitted(n0, l0, j0_n, n1, l1, j1_n): delta_j_n = abs(j1_n - j0_n) if delta_j_n > 2: @@ -202,36 +240,43 @@ def electric_quadrupole_permitted(n0, l0, j0_n, n1, l1, j1_n): if n0 == n1: return False - if not(electric_dipole_permitted(n0, l0, j0_n, n1, l1, j1_n) or \ - electric_quadrupole_permitted(n0, l0, j0_n, n1, l1, j1_n)): + if not ( + electric_dipole_permitted(n0, l0, j0_n, n1, l1, j1_n) + or electric_quadrupole_permitted(n0, l0, j0_n, n1, l1, j1_n) + ): return False return True def __repr__(self): def _format(value): - return '*' if value is None else value - - return '{}([n={}, l={}, j={}] -> [n={}, l={}, j={}])'\ - .format(self.__class__.__name__, - _format(self.source_principal_quantum_number), - _format(self.source_azimuthal_quantum_number), - _format(self.source_total_angular_momentum), - _format(self.destination_principal_quantum_number), - _format(self.destination_azimuthal_quantum_number), - _format(self.destination_total_angular_momentum)) + return "*" if value is None else value + + return "{}([n={}, l={}, j={}] -> [n={}, l={}, j={}])".format( + self.__class__.__name__, + _format(self.source_principal_quantum_number), + _format(self.source_azimuthal_quantum_number), + _format(self.source_total_angular_momentum), + _format(self.destination_principal_quantum_number), + _format(self.destination_azimuthal_quantum_number), + _format(self.destination_total_angular_momentum), + ) @property def source_subshell(self): - return AtomicSubshell(self.source_principal_quantum_number, - self.source_azimuthal_quantum_number, - self.source_total_angular_momentum_nominator) + return AtomicSubshell( + self.source_principal_quantum_number, + self.source_azimuthal_quantum_number, + self.source_total_angular_momentum_nominator, + ) @property def destination_subshell(self): - return AtomicSubshell(self.destination_principal_quantum_number, - self.destination_azimuthal_quantum_number, - self.destination_total_angular_momentum_nominator) + return AtomicSubshell( + self.destination_principal_quantum_number, + self.destination_azimuthal_quantum_number, + self.destination_total_angular_momentum_nominator, + ) @property def source_total_angular_momentum(self): @@ -245,6 +290,7 @@ def destination_total_angular_momentum(self): return None return self.destination_total_angular_momentum_nominator / 2.0 + @dataclasses.dataclass(frozen=True) class XrayLine: element: Element @@ -257,12 +303,12 @@ class XrayLine: def __post_init__(self): if not isinstance(self.element, Element): - object.__setattr__(self, 'element', Element(self.element)) + object.__setattr__(self, "element", Element(self.element)) if not isinstance(self.transition, XrayTransition): - object.__setattr__(self, 'transition', XrayTransition(self.transition)) + object.__setattr__(self, "transition", XrayTransition(self.transition)) def __repr__(self): - return '{}({})'.format(self.__class__.__name__, self.iupac) + return "{}({})".format(self.__class__.__name__, self.iupac) @property def atomic_number(self): @@ -272,6 +318,7 @@ def atomic_number(self): def z(self): return self.element.atomic_number + @dataclasses.dataclass(frozen=True) class Language: key: str @@ -279,12 +326,13 @@ class Language: def __post_init__(self): lencode = len(self.key) if lencode < 2 or lencode > 3: - raise ValueError('Code must be between 2 and 3 characters') + raise ValueError("Code must be between 2 and 3 characters") - object.__setattr__(self, 'key', self.key.lower()) + object.__setattr__(self, "key", self.key.lower()) def __repr__(self): - return '{}({})'.format(self.__class__.__name__, self.key) + return "{}({})".format(self.__class__.__name__, self.key) + @dataclasses.dataclass(frozen=True) class Notation: @@ -292,12 +340,13 @@ class Notation: def __post_init__(self): if not self.key: - raise ValueError('Name cannot be empty') + raise ValueError("Name cannot be empty") - object.__setattr__(self, 'key', self.key.lower()) + object.__setattr__(self, "key", self.key.lower()) def __repr__(self): - return '{}({})'.format(self.__class__.__name__, self.key) + return "{}({})".format(self.__class__.__name__, self.key) + @dataclasses.dataclass(frozen=True) class Reference: @@ -325,4 +374,4 @@ class Reference: doi: str = None def __repr__(self): - return '{}({})'.format(self.__class__.__name__, self.bibtexkey) + return "{}({})".format(self.__class__.__name__, self.bibtexkey) diff --git a/pyxray/parser/base.py b/pyxray/parser/base.py index 59a0041..ce64d44 100644 --- a/pyxray/parser/base.py +++ b/pyxray/parser/base.py @@ -11,7 +11,7 @@ from pyxray.descriptor import AtomicSubshell, XrayTransition, Notation, Reference # Globals and constants variables. -ENTRY_POINT = 'pyxray.parser' +ENTRY_POINT = "pyxray.parser" K = AtomicSubshell(1, 0, 1) L1 = AtomicSubshell(2, 0, 1) @@ -53,7 +53,7 @@ Q2 = AtomicSubshell(7, 1, 1) Q3 = AtomicSubshell(7, 1, 3) -Ka1 = XrayTransition(L3, K) +Ka1 = XrayTransition(L3, K) Ka2 = XrayTransition(L2, K) Ka = XrayTransition(2, 1, None, K) Kb1 = XrayTransition(M3, K) @@ -61,14 +61,14 @@ Kb2_2 = XrayTransition(N2, K) Kb2 = XrayTransition(4, 1, None, K) Kb3 = XrayTransition(M2, K) -Kb1_3 = XrayTransition(3, 1, None, K) # K-M2,3 +Kb1_3 = XrayTransition(3, 1, None, K) # K-M2,3 Kb4_1 = XrayTransition(N5, K) Kb4_2 = XrayTransition(N4, K) Kb4 = XrayTransition(4, 2, None, K) Kb5_1 = XrayTransition(M5, K) Kb5_2 = XrayTransition(M4, K) -Kb5 = XrayTransition(3, 2, None, K) # K-M4,5 -KO2_3 = XrayTransition(5, 1, None, K) # K-O2,3 +Kb5 = XrayTransition(3, 2, None, K) # K-M4,5 +KO2_3 = XrayTransition(5, 1, None, K) # K-O2,3 La1 = XrayTransition(M5, L3) La2 = XrayTransition(M4, L3) @@ -77,32 +77,32 @@ Lb2 = XrayTransition(N5, L3) Lb3 = XrayTransition(M3, L1) Lb4 = XrayTransition(M2, L1) -Lb3_4 = XrayTransition(3, 1, None, L1) # L1-M2,3 +Lb3_4 = XrayTransition(3, 1, None, L1) # L1-M2,3 Lb5_1 = XrayTransition(O5, L3) Lb5_2 = XrayTransition(O4, L3) -Lb5 = XrayTransition(5, 2, None, L3) # L3-O4,5 +Lb5 = XrayTransition(5, 2, None, L3) # L3-O4,5 Lb6 = XrayTransition(N1, L3) Lb7 = XrayTransition(O1, L3) Lb9 = XrayTransition(M5, L1) Lb10 = XrayTransition(M4, L1) Lb15 = XrayTransition(N4, L3) -Lb2_15 = XrayTransition(4, 2, None, L3) # L3-N4,5 +Lb2_15 = XrayTransition(4, 2, None, L3) # L3-N4,5 Lb17 = XrayTransition(M3, L2) Leta = XrayTransition(M1, L2) Lg1 = XrayTransition(N4, L2) Lg2 = XrayTransition(N2, L1) Lg3 = XrayTransition(N3, L1) -Lg2_3 = XrayTransition(4, 1, None, L1) # L1-N2,3 +Lg2_3 = XrayTransition(4, 1, None, L1) # L1-N2,3 Lg4 = XrayTransition(O3, L1) Lg4p = XrayTransition(O2, L1) Lg5 = XrayTransition(N1, L2) Lg6 = XrayTransition(O4, L2) Lg8 = XrayTransition(O1, L2) -Lg11 = XrayTransition (N5, L1) +Lg11 = XrayTransition(N5, L1) Ln = XrayTransition(M1, L2) Ll = XrayTransition(M1, L3) -Ll_n = XrayTransition(M1, 2, 1, None) # L2,3-M1 +Ll_n = XrayTransition(M1, 2, 1, None) # L2,3-M1 Ls = XrayTransition(M3, L3) Lt = XrayTransition(M2, L3) Lv = XrayTransition(N6, L2) @@ -118,27 +118,29 @@ Mz1 = XrayTransition(N3, M5) Mz2 = XrayTransition(N2, M4) Mz = XrayTransition(4, 1, None, 3, 2, None) -M1N2_3 = XrayTransition(4, 1, None, M1) # M1-N2,3 -M2_3M4_5 = XrayTransition(3, 2, None, 3, 1, None) # M2,3-M4,5 -M4_5O2_3 = XrayTransition(5, 1, None, 3, 2, None) # M4,5-O2,3 -M3O4_5 = XrayTransition(5, 2, None, M3) # M3-O4,5 -M4O2_3 = XrayTransition(5, 1, None, M4) # M4-O2,3 +M1N2_3 = XrayTransition(4, 1, None, M1) # M1-N2,3 +M2_3M4_5 = XrayTransition(3, 2, None, 3, 1, None) # M2,3-M4,5 +M4_5O2_3 = XrayTransition(5, 1, None, 3, 2, None) # M4,5-O2,3 +M3O4_5 = XrayTransition(5, 2, None, M3) # M3-O4,5 +M4O2_3 = XrayTransition(5, 1, None, M4) # M4-O2,3 -SIEGBAHN = Notation('siegbahn') -IUPAC = Notation('iupac') -ORBITAL = Notation('orbital') +SIEGBAHN = Notation("siegbahn") +IUPAC = Notation("iupac") +ORBITAL = Notation("orbital") -UNATTRIBUTED = Reference('unattributed') +UNATTRIBUTED = Reference("unattributed") # Install cache try: import requests_cache - dirpath = os.path.join(os.path.dirname(__file__), '..', 'data') - filepath = os.path.join(dirpath, 'cache') + + dirpath = os.path.join(os.path.dirname(__file__), "..", "data") + filepath = os.path.join(dirpath, "cache") requests_cache.install_cache(filepath) except ImportError: pass + class _Parser(collections.abc.Iterable, ProgressReportMixin): """ (abstract) Class to parse X-ray related information from a data source. @@ -154,8 +156,10 @@ def __iter__(self): Each parser should be registered in the setup.py under the entry point: `pyxray.parser` """ + pass + def find_parsers(): parsers = [] for entry_point in pkg_resources.iter_entry_points(ENTRY_POINT): diff --git a/pyxray/parser/campbell2001.py b/pyxray/parser/campbell2001.py index 3c5bf73..86c39c7 100644 --- a/pyxray/parser/campbell2001.py +++ b/pyxray/parser/campbell2001.py @@ -17,39 +17,72 @@ # Globals and constants variables. logger = logging.getLogger(__name__) -CAMPBELL2001 = Reference('campbell2001', - author='Campbell, J.L. and Papp, T.', - year=2001, - title='Widths of the atomic K-N7 levels', - booktitle='Atomic Data and Nuclear Data Tables', - pages='1-56', - volume=77) +CAMPBELL2001 = Reference( + "campbell2001", + author="Campbell, J.L. and Papp, T.", + year=2001, + title="Widths of the atomic K-N7 levels", + booktitle="Atomic Data and Nuclear Data Tables", + pages="1-56", + volume=77, +) _SUBSHELL_LOOKUP = { - 'K': base.K, - 'L1': base.L1, 'L2': base.L2, 'L3': base.L3, - 'M1': base.M1, 'M2': base.M2, 'M3': base.M3, 'M4': base.M4, 'M5': base.M5, - 'N1': base.N1, 'N2': base.N2, 'N3': base.N3, 'N4': base.N4, 'N5': base.N5, 'N6': base.N6, 'N7': base.N7 + "K": base.K, + "L1": base.L1, + "L2": base.L2, + "L3": base.L3, + "M1": base.M1, + "M2": base.M2, + "M3": base.M3, + "M4": base.M4, + "M5": base.M5, + "N1": base.N1, + "N2": base.N2, + "N3": base.N3, + "N4": base.N4, + "N5": base.N5, + "N6": base.N6, + "N7": base.N7, } -subshell_order = ['K', 'L1', 'L2', 'L3', 'M1', 'M2', 'M3', 'M4', 'M5', 'N1', 'N2', 'N3', 'N4', 'N5', 'N6', 'N7'] +subshell_order = [ + "K", + "L1", + "L2", + "L3", + "M1", + "M2", + "M3", + "M4", + "M5", + "N1", + "N2", + "N3", + "N4", + "N5", + "N6", + "N7", +] -class CampbellAtomicSubshellRadiativeWidthParser(base._Parser): +class CampbellAtomicSubshellRadiativeWidthParser(base._Parser): def __iter__(self): - relpath = os.path.join('..', 'data', 'campbell.asc') - content = pkgutil.get_data(__name__, relpath).decode('utf8') + relpath = os.path.join("..", "data", "campbell.asc") + content = pkgutil.get_data(__name__, relpath).decode("utf8") shell_width = [] for line in content.splitlines(): line = line.strip() - if not line: continue + if not line: + continue z = int(line[0:2]) for s in range(1, 17): - s_w = line[6 * s:6 * s + 6].strip() - if s_w == '': continue + s_w = line[6 * s : 6 * s + 6].strip() + if s_w == "": + continue shell_width.append([z, subshell_order[s - 1], float(s_w)]) length = len(shell_width) @@ -59,6 +92,6 @@ def __iter__(self): subshell = _SUBSHELL_LOOKUP[subshell] element = Element(z) prop = AtomicSubshellRadiativeWidth(CAMPBELL2001, element, subshell, width) - logger.debug('Parsed: {0}'.format(prop)) + logger.debug("Parsed: {0}".format(prop)) self.update(int((z - 1) / length * 100.0)) yield prop diff --git a/pyxray/parser/dtsa.py b/pyxray/parser/dtsa.py index 49f1aba..3864eea 100644 --- a/pyxray/parser/dtsa.py +++ b/pyxray/parser/dtsa.py @@ -12,35 +12,51 @@ # Local modules. from pyxray.descriptor import Reference, Element, XrayTransition -from pyxray.property import AtomicSubshellBindingEnergy, XrayTransitionEnergy, XrayTransitionRelativeWeight +from pyxray.property import ( + AtomicSubshellBindingEnergy, + XrayTransitionEnergy, + XrayTransitionRelativeWeight, +) import pyxray.parser.base as base # Globals and constants variables. logger = logging.getLogger(__name__) # noinspection PyArgumentList -DTSA1992 = Reference('dtsa1992', - author='C.E. Fiori and C.R. Swyt and R.L. Myklebust', - year=1992, - title='NIST/NIH Desk Top Spectrum Analyzer', - url='https://www.cstl.nist.gov/div837/Division/outputs/DTSA/oldDTSA.htm') +DTSA1992 = Reference( + "dtsa1992", + author="C.E. Fiori and C.R. Swyt and R.L. Myklebust", + year=1992, + title="NIST/NIH Desk Top Spectrum Analyzer", + url="https://www.cstl.nist.gov/div837/Division/outputs/DTSA/oldDTSA.htm", +) # noinspection SpellCheckingInspection _SUBSHELL_LOOKUP = { - 'Kedge': base.K, - 'LIedge': base.L1, 'LIIedge': base.L2, 'LIIIedge': base.L3, - 'MIedge': base.M1, 'MIIedge': base.M2, 'MIIIedge': base.M3, 'MIVedge': base.M4, 'MVedge': base.M5, - 'NIedge': base.N1, 'NIIedge': base.N2, 'NIIIedge': base.N3, 'NIVedge': base.N4, - 'NVedge': base.N5, 'NVIedge': base.N6, 'NVIIedge': base.N7, - 'OIedge': base.O1 + "Kedge": base.K, + "LIedge": base.L1, + "LIIedge": base.L2, + "LIIIedge": base.L3, + "MIedge": base.M1, + "MIIedge": base.M2, + "MIIIedge": base.M3, + "MIVedge": base.M4, + "MVedge": base.M5, + "NIedge": base.N1, + "NIIedge": base.N2, + "NIIIedge": base.N3, + "NIVedge": base.N4, + "NVedge": base.N5, + "NVIedge": base.N6, + "NVIIedge": base.N7, + "OIedge": base.O1, } # noinspection PyArgumentList class DtsaSubshellParser(base._Parser): - def __iter__(self): - relative_path = os.path.join('..', 'data', 'dtsa_subshell.csv') - content = pkgutil.get_data(__name__, relative_path).decode('utf8') + relative_path = os.path.join("..", "data", "dtsa_subshell.csv") + content = pkgutil.get_data(__name__, relative_path).decode("utf8") reader = csv.reader(content.splitlines()) # skip first line @@ -61,82 +77,100 @@ def __iter__(self): subshell = _SUBSHELL_LOOKUP[subshell_dtsa] element = Element(atomic_number) prop = AtomicSubshellBindingEnergy(DTSA1992, element, subshell, energy_eV) - logger.debug('Parsed: {0}'.format(prop)) + logger.debug("Parsed: {0}".format(prop)) self.update(int((atomic_number - 1) / length * 100.0)) yield prop -_TRANSITION_LOOKUP = { -'Ka1': base.Ka1, 'Ka2': base.Ka2, -'Kb1': base.Kb1, 'Kb2': base.Kb2, 'Kb3': base.Kb3, 'Kb4': base.Kb4, 'Kb5': base.Kb5, - -'La1': base.La1, 'La2': base.La2, -'Lb1': base.Lb1, 'Lb2': base.Lb2, 'Lb3': base.Lb3, 'Lb4': base.Lb4, 'Lb5': base.Lb5, -'Lb6': base.Lb6, 'Lb7': base.Lb7, 'Lb9': base.Lb9, 'Lb10': base.Lb10, -'Lb15': base.Lb15, 'Lb17': base.Lb17, -'Leta': base.Leta, -'Lg1': base.Lg1, 'Lg2': base.Lg2, 'Lg3': base.Lg3, 'Lg4': base.Lg4, -'Lg5': base.Lg5, 'Lg6': base.Lg6, 'Lg8': base.Lg8, -'Lg11': base.Lg11, -'Ll': base.Ll, 'Ls': base.Ls, 'Lt': base.Lt, 'Lv': base.Lv, 'Lu': base.Lu, - -'Ma1': base.Ma1, 'Ma2': base.Ma2, -'Mb': base.Mb, -'Mg': base.Mg, -'Mz1': base.Mz1, -'Mz2': base.Mz2, - -'N4-N6': XrayTransition(base.N6, base.N4), -'N5-N6': XrayTransition(base.N6, base.N5), - -#'Kb2': 'Kb2I', -#'Kb4': 'Kb4I', -#'Kb5': 'Kb5I', -#SKa,SKa',SKa'',SKa3,SKa3',SKa3'',SKa4,SKa5,SKa6,SKa7,SKa8,SKa9,SKb',SKb'',SKb+4,SKb+5,SKb7,SKb8,SKb9,SKbN,SKbX,SLa',SLa+IX,SLa+X,SLa+Y,SLa1+Z,SLa2',SLa3,SLa3+Z,SLa4,SLa5,SLa6,SLa7,SLa8,SLa9,SLaa,SLas,SLb'',SLb1+4,SLb14,SLb2+1,SLb2+2,SLb2+3,SLb2+4,SLb2+5,SLb2+7,SLb2+A,SLb2+B,SLb2+C,SLb5+1,SLb5+2,SLg1',SLg10,SLg2',SLg2'',SLg9,SMa+1,SMa+2,SMa+3,SMa+4,SMb1,SMb2,SMb3,SMg',Skb,Skb10,Skb6 - -'L1M1': XrayTransition(base.L1, base.M1), -'L1N1': XrayTransition(base.N1, base.L1), -'L1N4': XrayTransition(base.N4, base.L1), -'L1O1': XrayTransition(base.O1, base.L1), -'L1O4': XrayTransition(base.O4, base.L1), -'L2M2': XrayTransition(base.L2, base.M2), -'L2M5': XrayTransition(base.M5, base.L2), -'L2N2': XrayTransition(base.N2, base.L2), -'L2N3': XrayTransition(base.N3, base.L2), -'L2N5': XrayTransition(base.N5, base.L2), -'L2O2': XrayTransition(base.O2, base.L2), -'L2O3': XrayTransition(base.O3, base.L2), -'L2P2': XrayTransition(base.P2, base.L2), -'L2P3': XrayTransition(base.P3, base.L2), -'L3N2': XrayTransition(base.N2, base.L3), -'L3N3': XrayTransition(base.N3, base.L3), -'L3O2': XrayTransition(base.O2, base.L3), -'L3O3': XrayTransition(base.O3, base.L3), -'L3P1': XrayTransition(base.P1, base.L3), -'M2O4': XrayTransition(base.O4, base.M2), -'M1N2': XrayTransition(base.N2, base.M1), -'M1N3': XrayTransition(base.N3, base.M1), -'M2M4': XrayTransition(base.M4, base.M2), -'M2N1': XrayTransition(base.N1, base.M2), -'M2N4': XrayTransition(base.N4, base.M2), -'M3M4': XrayTransition(base.M4, base.M3), -'M3M5': XrayTransition(base.M5, base.M3), -'M3N1': XrayTransition(base.N1, base.M3), -'M3N4': XrayTransition(base.N4, base.M3), -'M3O1': XrayTransition(base.O1, base.M3), -'M3O4': XrayTransition(base.O4, base.M3), -'M3O5': XrayTransition(base.O5, base.M3), -'M4N3': XrayTransition(base.N3, base.M4), -'M4O2': XrayTransition(base.O2, base.M4), -'M5O3': XrayTransition(base.O3, base.M5), +_TRANSITION_LOOKUP = { + "Ka1": base.Ka1, + "Ka2": base.Ka2, + "Kb1": base.Kb1, + "Kb2": base.Kb2, + "Kb3": base.Kb3, + "Kb4": base.Kb4, + "Kb5": base.Kb5, + "La1": base.La1, + "La2": base.La2, + "Lb1": base.Lb1, + "Lb2": base.Lb2, + "Lb3": base.Lb3, + "Lb4": base.Lb4, + "Lb5": base.Lb5, + "Lb6": base.Lb6, + "Lb7": base.Lb7, + "Lb9": base.Lb9, + "Lb10": base.Lb10, + "Lb15": base.Lb15, + "Lb17": base.Lb17, + "Leta": base.Leta, + "Lg1": base.Lg1, + "Lg2": base.Lg2, + "Lg3": base.Lg3, + "Lg4": base.Lg4, + "Lg5": base.Lg5, + "Lg6": base.Lg6, + "Lg8": base.Lg8, + "Lg11": base.Lg11, + "Ll": base.Ll, + "Ls": base.Ls, + "Lt": base.Lt, + "Lv": base.Lv, + "Lu": base.Lu, + "Ma1": base.Ma1, + "Ma2": base.Ma2, + "Mb": base.Mb, + "Mg": base.Mg, + "Mz1": base.Mz1, + "Mz2": base.Mz2, + "N4-N6": XrayTransition(base.N6, base.N4), + "N5-N6": XrayTransition(base.N6, base.N5), + #'Kb2': 'Kb2I', + #'Kb4': 'Kb4I', + #'Kb5': 'Kb5I', + # SKa,SKa',SKa'',SKa3,SKa3',SKa3'',SKa4,SKa5,SKa6,SKa7,SKa8,SKa9,SKb',SKb'',SKb+4,SKb+5,SKb7,SKb8,SKb9,SKbN,SKbX,SLa',SLa+IX,SLa+X,SLa+Y,SLa1+Z,SLa2',SLa3,SLa3+Z,SLa4,SLa5,SLa6,SLa7,SLa8,SLa9,SLaa,SLas,SLb'',SLb1+4,SLb14,SLb2+1,SLb2+2,SLb2+3,SLb2+4,SLb2+5,SLb2+7,SLb2+A,SLb2+B,SLb2+C,SLb5+1,SLb5+2,SLg1',SLg10,SLg2',SLg2'',SLg9,SMa+1,SMa+2,SMa+3,SMa+4,SMb1,SMb2,SMb3,SMg',Skb,Skb10,Skb6 + "L1M1": XrayTransition(base.L1, base.M1), + "L1N1": XrayTransition(base.N1, base.L1), + "L1N4": XrayTransition(base.N4, base.L1), + "L1O1": XrayTransition(base.O1, base.L1), + "L1O4": XrayTransition(base.O4, base.L1), + "L2M2": XrayTransition(base.L2, base.M2), + "L2M5": XrayTransition(base.M5, base.L2), + "L2N2": XrayTransition(base.N2, base.L2), + "L2N3": XrayTransition(base.N3, base.L2), + "L2N5": XrayTransition(base.N5, base.L2), + "L2O2": XrayTransition(base.O2, base.L2), + "L2O3": XrayTransition(base.O3, base.L2), + "L2P2": XrayTransition(base.P2, base.L2), + "L2P3": XrayTransition(base.P3, base.L2), + "L3N2": XrayTransition(base.N2, base.L3), + "L3N3": XrayTransition(base.N3, base.L3), + "L3O2": XrayTransition(base.O2, base.L3), + "L3O3": XrayTransition(base.O3, base.L3), + "L3P1": XrayTransition(base.P1, base.L3), + "M2O4": XrayTransition(base.O4, base.M2), + "M1N2": XrayTransition(base.N2, base.M1), + "M1N3": XrayTransition(base.N3, base.M1), + "M2M4": XrayTransition(base.M4, base.M2), + "M2N1": XrayTransition(base.N1, base.M2), + "M2N4": XrayTransition(base.N4, base.M2), + "M3M4": XrayTransition(base.M4, base.M3), + "M3M5": XrayTransition(base.M5, base.M3), + "M3N1": XrayTransition(base.N1, base.M3), + "M3N4": XrayTransition(base.N4, base.M3), + "M3O1": XrayTransition(base.O1, base.M3), + "M3O4": XrayTransition(base.O4, base.M3), + "M3O5": XrayTransition(base.O5, base.M3), + "M4N3": XrayTransition(base.N3, base.M4), + "M4O2": XrayTransition(base.O2, base.M4), + "M5O3": XrayTransition(base.O3, base.M5), } # noinspection PyArgumentList class DtsaLineParser(base._Parser): - def __iter__(self): - relative_path = os.path.join('..', 'data', 'dtsa_line.csv') - content = pkgutil.get_data(__name__, relative_path).decode('utf8') + relative_path = os.path.join("..", "data", "dtsa_line.csv") + content = pkgutil.get_data(__name__, relative_path).decode("utf8") reader = csv.reader(content.splitlines()) # skip first line @@ -161,20 +195,20 @@ def __iter__(self): try: transition = _TRANSITION_LOOKUP[line_label] except KeyError: -# logger.debug('Line not found: {} for {}'.format(line_label, atomic_number)) + # logger.debug('Line not found: {} for {}'.format(line_label, atomic_number)) unparse_lines.add(line_label) continue element = Element(atomic_number) prop = XrayTransitionEnergy(DTSA1992, element, transition, energy_eV) -# logger.debug('Parsed: {0}'.format(prop)) + # logger.debug('Parsed: {0}'.format(prop)) self.update(int((atomic_number - 1) / length * 100.0)) yield prop prop = XrayTransitionRelativeWeight(DTSA1992, element, transition, fraction) -# logger.debug('Parsed: {0}'.format(prop)) + # logger.debug('Parsed: {0}'.format(prop)) self.update(int((atomic_number - 1) / length * 100.0)) yield prop - logger.debug('Lines not found: {}'.format(sorted(unparse_lines))) + logger.debug("Lines not found: {}".format(sorted(unparse_lines))) diff --git a/pyxray/parser/jeol.py b/pyxray/parser/jeol.py index 76a0086..eb314f9 100644 --- a/pyxray/parser/jeol.py +++ b/pyxray/parser/jeol.py @@ -18,90 +18,114 @@ # Globals and constants variables. logger = logging.getLogger(__name__) -JEOL = Reference('JEOL' - ) +JEOL = Reference("JEOL") _TRANSITION_LOOKUP = { -# Siegbahn -'KA1': base.Ka1, 'KA2': base.Ka2, -'KB1': base.Kb1, -'KB2': base.Kb2, 'KB2_1': base.Kb2_1, 'KB2+2': base.Kb2_2, 'KB2_2': base.Kb2_2, -'KB3': base.Kb3, -'KB4': base.Kb4, 'KB4_1': base.Kb4_1, 'KB4_2': base.Kb4_2, 'KB4x': base.Kb4_2, -'KB5': base.Kb5, 'KB5_1': base.Kb5_1, 'KB5+2': base.Kb5_2, 'KB5_2': base.Kb5_2, - -'LA1': base.La1, 'LA2': base.La2, -'LN': base.Ln, 'LL': base.Ll, 'LS': base.Ls, 'LT': base.Lt, 'LV': base.Lv, -'LB1': base.Lb1, 'LB2': base.Lb2, 'LB3': base.Lb3, 'LB4': base.Lb4, -'LB6': base.Lb6, 'LB7': base.Lb7, 'LB9': base.Lb9, 'LB10': base.Lb10, -'LB15': base.Lb15, 'LB17': base.Lb17, -'LG1': base.Lg1, 'LG2': base.Lg2, 'LG3': base.Lg3, 'LG4': base.Lg4, -'LG4_p': base.Lg4p, 'LG5': base.Lg5, 'LG6': base.Lg6, 'LG8': base.Lg8, - -'MA1': base.Ma1, 'MA2': base.Ma2, -'MB': base.Mb, -'MG': base.Mg, -'MZ1': base.Mz1, 'MZ2': base.Mz2, - -# IUPAC -'M1-N2': XrayTransition(base.N2, base.M1), -'M1-N3': XrayTransition(base.N3, base.M1), -'M2-M4': XrayTransition(base.M4, base.M2), -'M2-N1': XrayTransition(base.N1, base.M2), -'M2-N4': XrayTransition(base.N4, base.M2), -'M2-O4': XrayTransition(base.O4, base.M2), -'M3-M5': XrayTransition(base.M5, base.M3), -'M3-N1': XrayTransition(base.N1, base.M3), -'M3-N4': XrayTransition(base.N4, base.M3), -'M4-O2': XrayTransition(base.O2, base.M4), -'M4-O3': XrayTransition(base.O3, base.M4), -'M5-N1': XrayTransition(base.N1, base.M5), -'M5-O3': XrayTransition(base.O3, base.M5), -'N6-O4': XrayTransition(base.O4, base.N6), -'N7-O5': XrayTransition(base.O5, base.N7), - -# Set -#left out transition sets: KBX, KB5+, L2,3-M -'KA': base.Ka, -'KA1,2': base.Ka, -# 'KB': [(M3, K), (M2, K), (M5, K), (M4, K)], # FIXME: Not quite sure what to do with Kb -'KB1,3': base.Kb1_3, -'LA1,2': base.La, -'LB2,15': base.Lb2_15, -'LB5': base.Lb5, -'LB3,4': base.Lb3_4, -'LG2,3': base.Lg2_3, -'MA': base.Ma, -'MZ': base.Mz, - -'K-O2,3': base.KO2_3, -'M1-N2,3': base.M1N2_3, -'M2,3M4,5': base.M2_3M4_5, -'M4,5O2,3': base.M4_5O2_3, -'M3-O4,5': base.M3O4_5, -'M4-O2,3': base.M4O2_3, - -'LL,N': base.Ll_n, + # Siegbahn + "KA1": base.Ka1, + "KA2": base.Ka2, + "KB1": base.Kb1, + "KB2": base.Kb2, + "KB2_1": base.Kb2_1, + "KB2+2": base.Kb2_2, + "KB2_2": base.Kb2_2, + "KB3": base.Kb3, + "KB4": base.Kb4, + "KB4_1": base.Kb4_1, + "KB4_2": base.Kb4_2, + "KB4x": base.Kb4_2, + "KB5": base.Kb5, + "KB5_1": base.Kb5_1, + "KB5+2": base.Kb5_2, + "KB5_2": base.Kb5_2, + "LA1": base.La1, + "LA2": base.La2, + "LN": base.Ln, + "LL": base.Ll, + "LS": base.Ls, + "LT": base.Lt, + "LV": base.Lv, + "LB1": base.Lb1, + "LB2": base.Lb2, + "LB3": base.Lb3, + "LB4": base.Lb4, + "LB6": base.Lb6, + "LB7": base.Lb7, + "LB9": base.Lb9, + "LB10": base.Lb10, + "LB15": base.Lb15, + "LB17": base.Lb17, + "LG1": base.Lg1, + "LG2": base.Lg2, + "LG3": base.Lg3, + "LG4": base.Lg4, + "LG4_p": base.Lg4p, + "LG5": base.Lg5, + "LG6": base.Lg6, + "LG8": base.Lg8, + "MA1": base.Ma1, + "MA2": base.Ma2, + "MB": base.Mb, + "MG": base.Mg, + "MZ1": base.Mz1, + "MZ2": base.Mz2, + # IUPAC + "M1-N2": XrayTransition(base.N2, base.M1), + "M1-N3": XrayTransition(base.N3, base.M1), + "M2-M4": XrayTransition(base.M4, base.M2), + "M2-N1": XrayTransition(base.N1, base.M2), + "M2-N4": XrayTransition(base.N4, base.M2), + "M2-O4": XrayTransition(base.O4, base.M2), + "M3-M5": XrayTransition(base.M5, base.M3), + "M3-N1": XrayTransition(base.N1, base.M3), + "M3-N4": XrayTransition(base.N4, base.M3), + "M4-O2": XrayTransition(base.O2, base.M4), + "M4-O3": XrayTransition(base.O3, base.M4), + "M5-N1": XrayTransition(base.N1, base.M5), + "M5-O3": XrayTransition(base.O3, base.M5), + "N6-O4": XrayTransition(base.O4, base.N6), + "N7-O5": XrayTransition(base.O5, base.N7), + # Set + # left out transition sets: KBX, KB5+, L2,3-M + "KA": base.Ka, + "KA1,2": base.Ka, + # 'KB': [(M3, K), (M2, K), (M5, K), (M4, K)], # FIXME: Not quite sure what to do with Kb + "KB1,3": base.Kb1_3, + "LA1,2": base.La, + "LB2,15": base.Lb2_15, + "LB5": base.Lb5, + "LB3,4": base.Lb3_4, + "LG2,3": base.Lg2_3, + "MA": base.Ma, + "MZ": base.Mz, + "K-O2,3": base.KO2_3, + "M1-N2,3": base.M1N2_3, + "M2,3M4,5": base.M2_3M4_5, + "M4,5O2,3": base.M4_5O2_3, + "M3-O4,5": base.M3O4_5, + "M4-O2,3": base.M4O2_3, + "LL,N": base.Ll_n, } -class JEOLTransitionParser(base._Parser): +class JEOLTransitionParser(base._Parser): def __iter__(self): - relpath = os.path.join('..', 'data', 'lambda.asc') - content = pkgutil.get_data(__name__, relpath).decode('utf8') + relpath = os.path.join("..", "data", "lambda.asc") + content = pkgutil.get_data(__name__, relpath).decode("utf8") notread = set() transition_energy = [] for line in content.splitlines(): line = line.strip() - if not line: continue + if not line: + continue z = int(line[0:2]) siegbahn = line[10:18].strip() - if siegbahn.startswith('A'): # skip absorption edges + if siegbahn.startswith("A"): # skip absorption edges continue - if siegbahn.startswith('S'): # skip satellite lines + if siegbahn.startswith("S"): # skip satellite lines continue if siegbahn not in _TRANSITION_LOOKUP: # check for equivalence notread.add(siegbahn) @@ -127,12 +151,12 @@ def __iter__(self): element = Element(z) prop = XrayTransitionEnergy(JEOL, element, transition, eV) - logger.debug('Parsed: {0}'.format(prop)) + logger.debug("Parsed: {0}".format(prop)) self.update(int((z - 1) / length * 100.0)) yield prop prop = XrayTransitionRelativeWeight(JEOL, element, transition, probability) - logger.debug('Parsed: {0}'.format(prop)) + logger.debug("Parsed: {0}".format(prop)) self.update(int((z - 1) / length * 100.0)) yield prop diff --git a/pyxray/parser/nist.py b/pyxray/parser/nist.py index dd350f0..32e2c9c 100644 --- a/pyxray/parser/nist.py +++ b/pyxray/parser/nist.py @@ -18,17 +18,19 @@ # Globals and constants variables. logger = logging.getLogger(__name__) -NIST = Reference('coursey2015', - author='J. S. Coursey, D. J. Schwab, J. J. Tsai, R. A. Dragoset', - title='Atomic Weights and Isotopic Compositions', - organization='NIST Physical Measurement Laboratory', - year=2015) +NIST = Reference( + "coursey2015", + author="J. S. Coursey, D. J. Schwab, J. J. Tsai, R. A. Dragoset", + title="Atomic Weights and Isotopic Compositions", + organization="NIST Physical Measurement Laboratory", + year=2015, +) -class NISTElementAtomicWeightParser(base._Parser): +class NISTElementAtomicWeightParser(base._Parser): def __iter__(self): - relpath = os.path.join('..', 'data', 'nist_element_atomic_weight.html') - content = pkgutil.get_data(__name__, relpath).decode('utf8') + relpath = os.path.join("..", "data", "nist_element_atomic_weight.html") + content = pkgutil.get_data(__name__, relpath).decode("utf8") current_z = "1" value = 0 @@ -36,8 +38,12 @@ def __iter__(self): atomic_weights = [] for line in content.splitlines(): z = re.search(r"Atomic\sNumber\s=\s([0-9]*)", line) - relative_mass = re.search(r"Relative\sAtomic\sMass\s=\s([0-9]*.{0,1}[0-9]*)", line) - composition = re.search(r"Isotopic\sComposition\s=\s([0-9]*.{0,1}[0-9]*)", line) + relative_mass = re.search( + r"Relative\sAtomic\sMass\s=\s([0-9]*.{0,1}[0-9]*)", line + ) + composition = re.search( + r"Isotopic\sComposition\s=\s([0-9]*.{0,1}[0-9]*)", line + ) if z != None: if current_z != z.group(1): current_z = z.group(1) @@ -46,12 +52,12 @@ def __iter__(self): atomic_weights.append(value) value = 0 elif relative_mass != None: - if relative_mass.group(1) == '': + if relative_mass.group(1) == "": r_m = 0.0 else: r_m = float(relative_mass.group(1)) elif composition != None: - if composition.group(1) == '': + if composition.group(1) == "": c = 0.0 else: c = float(composition.group(1)) @@ -63,6 +69,6 @@ def __iter__(self): continue element = Element(z) prop = ElementAtomicWeight(NIST, element, aw) - logger.debug('Parsed: {0}'.format(prop)) + logger.debug("Parsed: {0}".format(prop)) self.update(int((z - 1) / length * 100.0)) yield prop diff --git a/pyxray/parser/notation.py b/pyxray/parser/notation.py index fec85c9..7264ca5 100644 --- a/pyxray/parser/notation.py +++ b/pyxray/parser/notation.py @@ -9,60 +9,182 @@ # Local modules. import pyxray.parser.base as base -from pyxray.descriptor import Element, Reference, XrayTransition, AtomicShell, AtomicSubshell, Notation -from pyxray.property import ElementSymbol, AtomicShellNotation, AtomicSubshellNotation, XrayTransitionNotation +from pyxray.descriptor import ( + Element, + Reference, + XrayTransition, + AtomicShell, + AtomicSubshell, + Notation, +) +from pyxray.property import ( + ElementSymbol, + AtomicShellNotation, + AtomicSubshellNotation, + XrayTransitionNotation, +) # Globals and constants variables. logger = logging.getLogger(__name__) -JENKINS1991 = Reference('jenkins1991', - author='Jenkins, R. and Manne, R. and Robin, R. and Senemaud, C.', - journal='X-Ray Spectrometry', - doi='10.1002/xrs.1300200308', - pages='149--155', - title='{IUPAC} --- nomenclature system for x-ray spectroscopy', - volume=20, - year=1991) - -BEARDEN1967 = Reference('bearden1967', - author='Bearden, J. A.', - journal='Rev. Mod. Phys.', - doi='10.1103/RevModPhys.39.78', - pages='78--124', - title='X-Ray Wavelengths', - volume=39, - year=1967) +JENKINS1991 = Reference( + "jenkins1991", + author="Jenkins, R. and Manne, R. and Robin, R. and Senemaud, C.", + journal="X-Ray Spectrometry", + doi="10.1002/xrs.1300200308", + pages="149--155", + title="{IUPAC} --- nomenclature system for x-ray spectroscopy", + volume=20, + year=1991, +) + +BEARDEN1967 = Reference( + "bearden1967", + author="Bearden, J. A.", + journal="Rev. Mod. Phys.", + doi="10.1103/RevModPhys.39.78", + pages="78--124", + title="X-Ray Wavelengths", + volume=39, + year=1967, +) + class ElementSymbolParser(base._Parser): SYMBOLS = [ - "H" , "He" , "Li" , "Be" , "B" , "C" , "N" , "O", - "F" , "Ne" , "Na" , "Mg" , "Al" , "Si" , "P" , "S", - "Cl" , "Ar" , "K" , "Ca" , "Sc" , "Ti" , "V" , "Cr", - "Mn" , "Fe" , "Co" , "Ni" , "Cu" , "Zn" , "Ga" , "Ge", - "As" , "Se" , "Br" , "Kr" , "Rb" , "Sr" , "Y" , "Zr", - "Nb" , "Mo" , "Tc" , "Ru" , "Rh" , "Pd" , "Ag" , "Cd", - "In" , "Sn" , "Sb" , "Te" , "I" , "Xe" , "Cs" , "Ba", - "La" , "Ce" , "Pr" , "Nd" , "Pm" , "Sm" , "Eu" , "Gd", - "Tb" , "Dy" , "Ho" , "Er" , "Tm" , "Yb" , "Lu" , "Hf", - "Ta" , "W" , "Re" , "Os" , "Ir" , "Pt" , "Au" , "Hg", - "Tl" , "Pb" , "Bi" , "Po" , "At" , "Rn" , "Fr" , "Ra", - "Ac" , "Th" , "Pa" , "U" , "Np" , "Pu" , "Am" , "Cm", - "Bk" , "Cf" , "Es" , "Fm" , "Md" , "No" , "Lr" , "Rf", - "Db" , "Sg" , "Bh" , "Hs" , "Mt" , "Ds" , "Rg" , "Cn", - "Uut", "Fl" , "Uup", "Lv" , "Uus", "Uuo" + "H", + "He", + "Li", + "Be", + "B", + "C", + "N", + "O", + "F", + "Ne", + "Na", + "Mg", + "Al", + "Si", + "P", + "S", + "Cl", + "Ar", + "K", + "Ca", + "Sc", + "Ti", + "V", + "Cr", + "Mn", + "Fe", + "Co", + "Ni", + "Cu", + "Zn", + "Ga", + "Ge", + "As", + "Se", + "Br", + "Kr", + "Rb", + "Sr", + "Y", + "Zr", + "Nb", + "Mo", + "Tc", + "Ru", + "Rh", + "Pd", + "Ag", + "Cd", + "In", + "Sn", + "Sb", + "Te", + "I", + "Xe", + "Cs", + "Ba", + "La", + "Ce", + "Pr", + "Nd", + "Pm", + "Sm", + "Eu", + "Gd", + "Tb", + "Dy", + "Ho", + "Er", + "Tm", + "Yb", + "Lu", + "Hf", + "Ta", + "W", + "Re", + "Os", + "Ir", + "Pt", + "Au", + "Hg", + "Tl", + "Pb", + "Bi", + "Po", + "At", + "Rn", + "Fr", + "Ra", + "Ac", + "Th", + "Pa", + "U", + "Np", + "Pu", + "Am", + "Cm", + "Bk", + "Cf", + "Es", + "Fm", + "Md", + "No", + "Lr", + "Rf", + "Db", + "Sg", + "Bh", + "Hs", + "Mt", + "Ds", + "Rg", + "Cn", + "Uut", + "Fl", + "Uup", + "Lv", + "Uus", + "Uuo", ] def __iter__(self): length = len(self.SYMBOLS) for z, symbol in enumerate(self.SYMBOLS, 1): prop = ElementSymbol(base.UNATTRIBUTED, Element(z), symbol) - logger.debug('Parsed: {0}'.format(prop)) + logger.debug("Parsed: {0}".format(prop)) self.update(int((z - 1) / length * 100.0)) yield prop + MAX_N = 7 + def iter_subshells(max_n): nprev = 0 for n in range(1, max_n + 1): @@ -76,6 +198,7 @@ def iter_subshells(max_n): i += 1 nprev = n + def iter_transitions(max_n): for src_n, src_l, src_j_n, src_i in iter_subshells(max_n): @@ -93,13 +216,16 @@ def iter_transitions(max_n): if src_n == dst_n and src_i <= dst_i: continue - yield XrayTransition(src_n, src_l, src_j_n, dst_n, dst_l, dst_j_n), src_i, dst_i + yield XrayTransition( + src_n, src_l, src_j_n, dst_n, dst_l, dst_j_n + ), src_i, dst_i + class AtomicShellNotationParser(base._Parser): - SIEGBAHN_SHELLS = ['K', 'L', 'M', 'N', 'O', 'P', 'Q'] - IUPAC_SHELLS = ['K', 'L', 'M', 'N', 'O', 'P', 'Q'] - ORBITAL_SHELLS = ['1', '2', '3', '4', '5', '6', '7'] + SIEGBAHN_SHELLS = ["K", "L", "M", "N", "O", "P", "Q"] + IUPAC_SHELLS = ["K", "L", "M", "N", "O", "P", "Q"] + ORBITAL_SHELLS = ["1", "2", "3", "4", "5", "6", "7"] @classmethod def _create_entry_siegbahn(cls, n): @@ -126,40 +252,57 @@ def __iter__(self): atomic_shell = AtomicShell(n) ascii, utf16, html, latex = self._create_entry_siegbahn(n) - prop = AtomicShellNotation(base.UNATTRIBUTED, - atomic_shell, - base.SIEGBAHN, - ascii, utf16, html, latex) - logger.debug('Parsed: {0}'.format(prop)) + prop = AtomicShellNotation( + base.UNATTRIBUTED, + atomic_shell, + base.SIEGBAHN, + ascii, + utf16, + html, + latex, + ) + logger.debug("Parsed: {0}".format(prop)) yield prop ascii, utf16, html, latex = self._create_entry_iupac(n) - prop = AtomicShellNotation(base.UNATTRIBUTED, - atomic_shell, - base.IUPAC, - ascii, utf16, html, latex) - logger.debug('Parsed: {0}'.format(prop)) + prop = AtomicShellNotation( + base.UNATTRIBUTED, atomic_shell, base.IUPAC, ascii, utf16, html, latex + ) + logger.debug("Parsed: {0}".format(prop)) yield prop ascii, utf16, html, latex = self._create_entry_orbital(n) - prop = AtomicShellNotation(base.UNATTRIBUTED, - atomic_shell, - base.ORBITAL, - ascii, utf16, html, latex) - logger.debug('Parsed: {0}'.format(prop)) + prop = AtomicShellNotation( + base.UNATTRIBUTED, atomic_shell, base.ORBITAL, ascii, utf16, html, latex + ) + logger.debug("Parsed: {0}".format(prop)) yield prop + class AtomicSubshellNotationParser(base._Parser): - SIEGBAHN_NUMBERS = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', - 'IX', 'X', 'XI', 'XII', 'XIII'] - ORBITAL_L = ['s', 'p', 'd', 'f', 'g', 'h', 'i'] + SIEGBAHN_NUMBERS = [ + "I", + "II", + "III", + "IV", + "V", + "VI", + "VII", + "VIII", + "IX", + "X", + "XI", + "XII", + "XIII", + ] + ORBITAL_L = ["s", "p", "d", "f", "g", "h", "i"] @classmethod def _create_entry_siegbahn(cls, n, l, j_n, i): shell = AtomicShellNotationParser.SIEGBAHN_SHELLS[n - 1] s = shell - if n != 1: # No KI + if n != 1: # No KI s += cls.SIEGBAHN_NUMBERS[i - 1] return s, s, s, s @@ -167,18 +310,18 @@ def _create_entry_siegbahn(cls, n, l, j_n, i): def _create_entry_iupac(cls, n, l, j_n, i): shell = AtomicShellNotationParser.IUPAC_SHELLS[n - 1] s = html = latex = shell - if n != 1: # No K1 + if n != 1: # No K1 s += str(i) - html += '{0:d}'.format(i) - latex += '$_{{{0:d}}}$'.format(i) + html += "{0:d}".format(i) + latex += "$_{{{0:d}}}$".format(i) return s, s, html, latex @classmethod def _create_entry_orbital(cls, n, l, j_n, i): l_letter = cls.ORBITAL_L[l] - s = '{0:d}{1}{2}/2'.format(n, l_letter, j_n) - html = '{0:d}{1}{2}/2'.format(n, l_letter, j_n) - latex = '{0:d}{1}$_{{{2}/2}}$'.format(n, l_letter, j_n) + s = "{0:d}{1}{2}/2".format(n, l_letter, j_n) + html = "{0:d}{1}{2}/2".format(n, l_letter, j_n) + latex = "{0:d}{1}$_{{{2}/2}}$".format(n, l_letter, j_n) return s, s, html, latex def __iter__(self): @@ -187,35 +330,47 @@ def __iter__(self): atomic_subshell = AtomicSubshell(n, l, j_n) - ascii, utf16, html, latex = \ - self._create_entry_siegbahn(n, l, j_n, i) - prop = AtomicSubshellNotation(base.UNATTRIBUTED, - atomic_subshell, - base.SIEGBAHN, - ascii, utf16, html, latex) - logger.debug('Parsed: {0}'.format(prop)) + ascii, utf16, html, latex = self._create_entry_siegbahn(n, l, j_n, i) + prop = AtomicSubshellNotation( + base.UNATTRIBUTED, + atomic_subshell, + base.SIEGBAHN, + ascii, + utf16, + html, + latex, + ) + logger.debug("Parsed: {0}".format(prop)) yield prop - ascii, utf16, html, latex = \ - self._create_entry_iupac(n, l, j_n, i) - prop = AtomicSubshellNotation(base.UNATTRIBUTED, - atomic_subshell, - base.IUPAC, - ascii, utf16, html, latex) - logger.debug('Parsed: {0}'.format(prop)) + ascii, utf16, html, latex = self._create_entry_iupac(n, l, j_n, i) + prop = AtomicSubshellNotation( + base.UNATTRIBUTED, + atomic_subshell, + base.IUPAC, + ascii, + utf16, + html, + latex, + ) + logger.debug("Parsed: {0}".format(prop)) yield prop - ascii, utf16, html, latex = \ - self._create_entry_orbital(n, l, j_n, i) - prop = AtomicSubshellNotation(base.UNATTRIBUTED, - atomic_subshell, - base.ORBITAL, - ascii, utf16, html, latex) - logger.debug('Parsed: {0}'.format(prop)) + ascii, utf16, html, latex = self._create_entry_orbital(n, l, j_n, i) + prop = AtomicSubshellNotation( + base.UNATTRIBUTED, + atomic_subshell, + base.ORBITAL, + ascii, + utf16, + html, + latex, + ) + logger.debug("Parsed: {0}".format(prop)) yield prop -class GenericXrayTransitionNotationParser(base._Parser): +class GenericXrayTransitionNotationParser(base._Parser): def __iter__(self): length = (MAX_N * MAX_N) ** 2 progress = 0 @@ -226,595 +381,919 @@ def __iter__(self): src = transition.source_subshell dst = transition.destination_subshell - ascii0, utf0, html0, latex0 = \ - AtomicSubshellNotationParser._create_entry_iupac(src.n, src.l, src.j_n, src_i) - ascii1, utf1, html1, latex1 = \ - AtomicSubshellNotationParser._create_entry_iupac(dst.n, dst.l, dst.j_n, dst_i) - - ascii = '{0}-{1}'.format(ascii1, ascii0) - utf = '{0}\u2013{1}'.format(utf1, utf0) - html = '{0}–{1}'.format(html1, html0) - latex = '{0}--{1}'.format(latex1, latex0) - - prop = XrayTransitionNotation(base.UNATTRIBUTED, - transition, - base.IUPAC, - ascii, utf, html, latex) - logger.debug('Parsed: {0}'.format(prop)) + ( + ascii0, + utf0, + html0, + latex0, + ) = AtomicSubshellNotationParser._create_entry_iupac( + src.n, src.l, src.j_n, src_i + ) + ( + ascii1, + utf1, + html1, + latex1, + ) = AtomicSubshellNotationParser._create_entry_iupac( + dst.n, dst.l, dst.j_n, dst_i + ) + + ascii = "{0}-{1}".format(ascii1, ascii0) + utf = "{0}\u2013{1}".format(utf1, utf0) + html = "{0}–{1}".format(html1, html0) + latex = "{0}--{1}".format(latex1, latex0) + + prop = XrayTransitionNotation( + base.UNATTRIBUTED, transition, base.IUPAC, ascii, utf, html, latex + ) + logger.debug("Parsed: {0}".format(prop)) yield prop -class KnownXrayTransitionNotationParser(base._Parser): +class KnownXrayTransitionNotationParser(base._Parser): def __iter__(self): assert base.Ka1.source_subshell == base.L3 assert base.Ka1.destination_subshell == base.K - yield XrayTransitionNotation(JENKINS1991, base.Ka1, base.SIEGBAHN, - 'Ka1', - 'K\u03b11', - 'Kα1', - '\\ensuremath{\\mathrm{K}\\alpha_1}') + yield XrayTransitionNotation( + JENKINS1991, + base.Ka1, + base.SIEGBAHN, + "Ka1", + "K\u03b11", + "Kα1", + "\\ensuremath{\\mathrm{K}\\alpha_1}", + ) assert base.Ka2.source_subshell == base.L2 assert base.Ka2.destination_subshell == base.K - yield XrayTransitionNotation(JENKINS1991, base.Ka2, base.SIEGBAHN, - 'Ka2', - 'K\u03b12', - 'Kα2', - '\\ensuremath{\\mathrm{K}\\alpha_2}') + yield XrayTransitionNotation( + JENKINS1991, + base.Ka2, + base.SIEGBAHN, + "Ka2", + "K\u03b12", + "Kα2", + "\\ensuremath{\\mathrm{K}\\alpha_2}", + ) assert base.Ka.destination_subshell == base.K - yield XrayTransitionNotation(JENKINS1991, base.Ka, base.SIEGBAHN, - 'Ka', - 'K\u03b1', - 'Kα', - '\\ensuremath{\\mathrm{K}\\alpha}') - yield XrayTransitionNotation(JENKINS1991, base.Ka, base.IUPAC, - 'K-L(2,3)', - 'K\u2013L(2,3)', - 'K–L2,3', - '\\ensuremath{\\mathrm{K}}--\\ensuremath{\\mathrm{L}_{2,3}}') + yield XrayTransitionNotation( + JENKINS1991, + base.Ka, + base.SIEGBAHN, + "Ka", + "K\u03b1", + "Kα", + "\\ensuremath{\\mathrm{K}\\alpha}", + ) + yield XrayTransitionNotation( + JENKINS1991, + base.Ka, + base.IUPAC, + "K-L(2,3)", + "K\u2013L(2,3)", + "K–L2,3", + "\\ensuremath{\\mathrm{K}}--\\ensuremath{\\mathrm{L}_{2,3}}", + ) assert base.Kb1.source_subshell == base.M3 assert base.Kb1.destination_subshell == base.K - yield XrayTransitionNotation(JENKINS1991, base.Kb1, base.SIEGBAHN, - 'Kb1', - 'K\u03b21', - 'Kβ1', - '\\ensuremath{\\mathrm{K}\\beta_1}') + yield XrayTransitionNotation( + JENKINS1991, + base.Kb1, + base.SIEGBAHN, + "Kb1", + "K\u03b21", + "Kβ1", + "\\ensuremath{\\mathrm{K}\\beta_1}", + ) assert base.Kb2_1.source_subshell == base.N3 assert base.Kb2_1.destination_subshell == base.K - yield XrayTransitionNotation(JENKINS1991, base.Kb2_1, base.SIEGBAHN, - 'Kb2I', - 'K\u03b22I', - 'Kβ2I', - '\\ensuremath{\\mathrm{K}\\beta_2^I}') + yield XrayTransitionNotation( + JENKINS1991, + base.Kb2_1, + base.SIEGBAHN, + "Kb2I", + "K\u03b22I", + "Kβ2I", + "\\ensuremath{\\mathrm{K}\\beta_2^I}", + ) assert base.Kb2_2.source_subshell == base.N2 assert base.Kb2_2.destination_subshell == base.K - yield XrayTransitionNotation(JENKINS1991, base.Kb2_2, base.SIEGBAHN, - 'Kb2II', - 'K\u03b22II', - 'Kβ2II', - '\\ensuremath{\\mathrm{K}\\beta_2^{II}}') + yield XrayTransitionNotation( + JENKINS1991, + base.Kb2_2, + base.SIEGBAHN, + "Kb2II", + "K\u03b22II", + "Kβ2II", + "\\ensuremath{\\mathrm{K}\\beta_2^{II}}", + ) assert base.Kb2.destination_subshell == base.K - yield XrayTransitionNotation(JENKINS1991, base.Kb2, base.SIEGBAHN, - 'Kb2', - 'K\u03b22', - 'Kβ2', - '\\ensuremath{\\mathrm{K}\\beta_2}') + yield XrayTransitionNotation( + JENKINS1991, + base.Kb2, + base.SIEGBAHN, + "Kb2", + "K\u03b22", + "Kβ2", + "\\ensuremath{\\mathrm{K}\\beta_2}", + ) assert base.Kb3.source_subshell == base.M2 assert base.Kb3.destination_subshell == base.K - yield XrayTransitionNotation(JENKINS1991, base.Kb3, base.SIEGBAHN, - 'Kb3', - 'K\u03b23', - 'Kβ3', - '\\ensuremath{\\mathrm{K}\\beta_3}') + yield XrayTransitionNotation( + JENKINS1991, + base.Kb3, + base.SIEGBAHN, + "Kb3", + "K\u03b23", + "Kβ3", + "\\ensuremath{\\mathrm{K}\\beta_3}", + ) assert base.Kb1_3.destination_subshell == base.K - yield XrayTransitionNotation(base.UNATTRIBUTED, base.Kb1_3, base.SIEGBAHN, - 'Kb1,3', - 'K\u03b21,3', - 'Kβ1,3', - '\\ensuremath{\\mathrm{K}\\beta_{1,3}}') - yield XrayTransitionNotation(base.UNATTRIBUTED, base.Kb1_3, base.IUPAC, - 'K-M(2,3)', - 'K\u2013M(2,3)', - 'K–M2,3', - '\\ensuremath{\\mathrm{K}}--\\ensuremath{\\mathrm{M}_{2,3}}') + yield XrayTransitionNotation( + base.UNATTRIBUTED, + base.Kb1_3, + base.SIEGBAHN, + "Kb1,3", + "K\u03b21,3", + "Kβ1,3", + "\\ensuremath{\\mathrm{K}\\beta_{1,3}}", + ) + yield XrayTransitionNotation( + base.UNATTRIBUTED, + base.Kb1_3, + base.IUPAC, + "K-M(2,3)", + "K\u2013M(2,3)", + "K–M2,3", + "\\ensuremath{\\mathrm{K}}--\\ensuremath{\\mathrm{M}_{2,3}}", + ) assert base.Kb4_1.source_subshell == base.N5 assert base.Kb4_1.destination_subshell == base.K - yield XrayTransitionNotation(JENKINS1991, base.Kb4_1, base.SIEGBAHN, - 'Kb4I', - 'K\u03b24II', - 'Kβ4I', - '\\ensuremath{\\mathrm{K}\\beta_4^I}') + yield XrayTransitionNotation( + JENKINS1991, + base.Kb4_1, + base.SIEGBAHN, + "Kb4I", + "K\u03b24II", + "Kβ4I", + "\\ensuremath{\\mathrm{K}\\beta_4^I}", + ) assert base.Kb4_2.source_subshell == base.N4 assert base.Kb4_2.destination_subshell == base.K - yield XrayTransitionNotation(JENKINS1991, base.Kb4_2, base.SIEGBAHN, - 'Kb4II', - 'K\u03b24II', - 'Kβ4II', - '\\ensuremath{\\mathrm{K}\\beta_4^{II}}') + yield XrayTransitionNotation( + JENKINS1991, + base.Kb4_2, + base.SIEGBAHN, + "Kb4II", + "K\u03b24II", + "Kβ4II", + "\\ensuremath{\\mathrm{K}\\beta_4^{II}}", + ) assert base.Kb4.destination_subshell == base.K - yield XrayTransitionNotation(JENKINS1991, base.Kb4, base.SIEGBAHN, - 'Kb4', - 'K\u03b24', - 'Kβ4', - '\\ensuremath{\\mathrm{K}\\beta_4}') + yield XrayTransitionNotation( + JENKINS1991, + base.Kb4, + base.SIEGBAHN, + "Kb4", + "K\u03b24", + "Kβ4", + "\\ensuremath{\\mathrm{K}\\beta_4}", + ) assert base.Kb5_1.source_subshell == base.M5 assert base.Kb5_1.destination_subshell == base.K - yield XrayTransitionNotation(JENKINS1991, base.Kb5_1, base.SIEGBAHN, - 'Kb5I', - 'K\u03b25I', - 'Kβ5I', - '\\ensuremath{\\mathrm{K}\\beta_5^I}') + yield XrayTransitionNotation( + JENKINS1991, + base.Kb5_1, + base.SIEGBAHN, + "Kb5I", + "K\u03b25I", + "Kβ5I", + "\\ensuremath{\\mathrm{K}\\beta_5^I}", + ) assert base.Kb5_2.source_subshell == base.M4 assert base.Kb5_2.destination_subshell == base.K - yield XrayTransitionNotation(JENKINS1991, base.Kb5_2, base.SIEGBAHN, - 'Kb5II', - 'K\u03b25II', - 'Kβ5II', - '\\ensuremath{\\mathrm{K}\\beta_5^{II}}') + yield XrayTransitionNotation( + JENKINS1991, + base.Kb5_2, + base.SIEGBAHN, + "Kb5II", + "K\u03b25II", + "Kβ5II", + "\\ensuremath{\\mathrm{K}\\beta_5^{II}}", + ) assert base.Kb5.destination_subshell == base.K - yield XrayTransitionNotation(JENKINS1991, base.Kb5, base.SIEGBAHN, - 'Kb5', - 'K\u03b25', - 'Kβ5', - '\\ensuremath{\\mathrm{K}\\beta_5}') - yield XrayTransitionNotation(base.UNATTRIBUTED, base.Kb5, base.IUPAC, - 'K-M(4,5)', - 'K\u2013M(4,5)', - 'K–M4,5', - '\\ensuremath{\\mathrm{K}}--\\ensuremath{\\mathrm{M}_{4,5}}') + yield XrayTransitionNotation( + JENKINS1991, + base.Kb5, + base.SIEGBAHN, + "Kb5", + "K\u03b25", + "Kβ5", + "\\ensuremath{\\mathrm{K}\\beta_5}", + ) + yield XrayTransitionNotation( + base.UNATTRIBUTED, + base.Kb5, + base.IUPAC, + "K-M(4,5)", + "K\u2013M(4,5)", + "K–M4,5", + "\\ensuremath{\\mathrm{K}}--\\ensuremath{\\mathrm{M}_{4,5}}", + ) assert base.KO2_3.destination_subshell == base.K - yield XrayTransitionNotation(base.UNATTRIBUTED, base.KO2_3, base.IUPAC, - 'K-O(2,3)', - 'K\u2013O(2,3)', - 'K–O2,3', - '\\ensuremath{\\mathrm{K}}--\\ensuremath{\\mathrm{O}_{2,3}}') + yield XrayTransitionNotation( + base.UNATTRIBUTED, + base.KO2_3, + base.IUPAC, + "K-O(2,3)", + "K\u2013O(2,3)", + "K–O2,3", + "\\ensuremath{\\mathrm{K}}--\\ensuremath{\\mathrm{O}_{2,3}}", + ) assert base.La1.source_subshell == base.M5 assert base.La1.destination_subshell == base.L3 - yield XrayTransitionNotation(JENKINS1991, base.La1, base.SIEGBAHN, - 'La1', - 'L\u03b11', - 'Lα1', - '\\ensuremath{\\mathrm{L}\\alpha_1}') + yield XrayTransitionNotation( + JENKINS1991, + base.La1, + base.SIEGBAHN, + "La1", + "L\u03b11", + "Lα1", + "\\ensuremath{\\mathrm{L}\\alpha_1}", + ) assert base.La2.source_subshell == base.M4 assert base.La2.destination_subshell == base.L3 - yield XrayTransitionNotation(JENKINS1991, base.La2, base.SIEGBAHN, - 'La2', - 'L\u03b12', - 'Lα2', - '\\ensuremath{\\mathrm{L}\\alpha_2}') + yield XrayTransitionNotation( + JENKINS1991, + base.La2, + base.SIEGBAHN, + "La2", + "L\u03b12", + "Lα2", + "\\ensuremath{\\mathrm{L}\\alpha_2}", + ) assert base.La.destination_subshell == base.L3 - yield XrayTransitionNotation(JENKINS1991, base.La, base.SIEGBAHN, - 'La', - 'L\u03b1', - 'Lα', - '\\ensuremath{\\mathrm{L}\\alpha}') - yield XrayTransitionNotation(JENKINS1991, base.La, base.IUPAC, - 'L3-M(4,5)', - 'L3\u2013M(4,5)', - 'L3–M4,5', - '\\ensuremath{\\mathrm{L}_3}--\\ensuremath{\\mathrm{M}_{4,5}}') + yield XrayTransitionNotation( + JENKINS1991, + base.La, + base.SIEGBAHN, + "La", + "L\u03b1", + "Lα", + "\\ensuremath{\\mathrm{L}\\alpha}", + ) + yield XrayTransitionNotation( + JENKINS1991, + base.La, + base.IUPAC, + "L3-M(4,5)", + "L3\u2013M(4,5)", + "L3–M4,5", + "\\ensuremath{\\mathrm{L}_3}--\\ensuremath{\\mathrm{M}_{4,5}}", + ) assert base.Lb1.source_subshell == base.M4 assert base.Lb1.destination_subshell == base.L2 - yield XrayTransitionNotation(JENKINS1991, base.Lb1, base.SIEGBAHN, - 'Lb1', - 'L\u03b21', - 'Lβ1', - '\\ensuremath{\\mathrm{L}\\beta_1}') + yield XrayTransitionNotation( + JENKINS1991, + base.Lb1, + base.SIEGBAHN, + "Lb1", + "L\u03b21", + "Lβ1", + "\\ensuremath{\\mathrm{L}\\beta_1}", + ) assert base.Lb2.source_subshell == base.N5 assert base.Lb2.destination_subshell == base.L3 - yield XrayTransitionNotation(JENKINS1991, base.Lb2, base.SIEGBAHN, - 'Lb2', - 'L\u03b22', - 'Lβ2', - '\\ensuremath{\\mathrm{L}\\beta_2}') + yield XrayTransitionNotation( + JENKINS1991, + base.Lb2, + base.SIEGBAHN, + "Lb2", + "L\u03b22", + "Lβ2", + "\\ensuremath{\\mathrm{L}\\beta_2}", + ) assert base.Lb3.source_subshell == base.M3 assert base.Lb3.destination_subshell == base.L1 - yield XrayTransitionNotation(JENKINS1991, base.Lb3, base.SIEGBAHN, - 'Lb3', - 'L\u03b23', - 'Lβ3', - '\\ensuremath{\\mathrm{L}\\beta_3}') + yield XrayTransitionNotation( + JENKINS1991, + base.Lb3, + base.SIEGBAHN, + "Lb3", + "L\u03b23", + "Lβ3", + "\\ensuremath{\\mathrm{L}\\beta_3}", + ) assert base.Lb4.source_subshell == base.M2 assert base.Lb4.destination_subshell == base.L1 - yield XrayTransitionNotation(JENKINS1991, base.Lb4, base.SIEGBAHN, - 'Lb4', - 'L\u03b24', - 'Lβ4', - '\\ensuremath{\\mathrm{L}\\beta_4}') + yield XrayTransitionNotation( + JENKINS1991, + base.Lb4, + base.SIEGBAHN, + "Lb4", + "L\u03b24", + "Lβ4", + "\\ensuremath{\\mathrm{L}\\beta_4}", + ) assert base.Lb3_4.destination_subshell == base.L1 - yield XrayTransitionNotation(base.UNATTRIBUTED, base.Lb3_4, base.SIEGBAHN, - 'Lb3,4', - 'L\u03b23,4', - 'Lβ3,4', - '\\ensuremath{\\mathrm{L}\\beta_{3,4}}') - yield XrayTransitionNotation(base.UNATTRIBUTED, base.Lb3_4, base.IUPAC, - 'L1-M(2,3)', - 'L1\u2013M(2,3)', - 'L1–M2,3', - '\\ensuremath{\\mathrm{L}_1}--\\ensuremath{\\mathrm{M}_{2,3}}') + yield XrayTransitionNotation( + base.UNATTRIBUTED, + base.Lb3_4, + base.SIEGBAHN, + "Lb3,4", + "L\u03b23,4", + "Lβ3,4", + "\\ensuremath{\\mathrm{L}\\beta_{3,4}}", + ) + yield XrayTransitionNotation( + base.UNATTRIBUTED, + base.Lb3_4, + base.IUPAC, + "L1-M(2,3)", + "L1\u2013M(2,3)", + "L1–M2,3", + "\\ensuremath{\\mathrm{L}_1}--\\ensuremath{\\mathrm{M}_{2,3}}", + ) assert base.Lb5_1.source_subshell == base.O5 assert base.Lb5_1.destination_subshell == base.L3 - yield XrayTransitionNotation(BEARDEN1967, base.Lb5_1, base.SIEGBAHN, - 'Lb5I', - 'L\u03b25I', - 'Lβ5I', - '\\ensuremath{\\mathrm{L}\\beta_5^I}') + yield XrayTransitionNotation( + BEARDEN1967, + base.Lb5_1, + base.SIEGBAHN, + "Lb5I", + "L\u03b25I", + "Lβ5I", + "\\ensuremath{\\mathrm{L}\\beta_5^I}", + ) assert base.Lb5_2.source_subshell == base.O4 assert base.Lb5_2.destination_subshell == base.L3 - yield XrayTransitionNotation(BEARDEN1967, base.Lb5_2, base.SIEGBAHN, - 'Lb5II', - 'L\u03b25II', - 'Lβ5II', - '\\ensuremath{\\mathrm{L}\\beta_5^{II}}') + yield XrayTransitionNotation( + BEARDEN1967, + base.Lb5_2, + base.SIEGBAHN, + "Lb5II", + "L\u03b25II", + "Lβ5II", + "\\ensuremath{\\mathrm{L}\\beta_5^{II}}", + ) assert base.Lb5.destination_subshell == base.L3 - yield XrayTransitionNotation(BEARDEN1967, base.Lb5, base.SIEGBAHN, - 'Lb5', - 'L\u03b25', - 'Lβ5', - '\\ensuremath{\\mathrm{L}\\beta_5}') + yield XrayTransitionNotation( + BEARDEN1967, + base.Lb5, + base.SIEGBAHN, + "Lb5", + "L\u03b25", + "Lβ5", + "\\ensuremath{\\mathrm{L}\\beta_5}", + ) assert base.Lb6.source_subshell == base.N1 assert base.Lb6.destination_subshell == base.L3 - yield XrayTransitionNotation(JENKINS1991, base.Lb6, base.SIEGBAHN, - 'Lb6', - 'L\u03b26', - 'Lβ6', - '\\ensuremath{\\mathrm{L}\\beta_6}') + yield XrayTransitionNotation( + JENKINS1991, + base.Lb6, + base.SIEGBAHN, + "Lb6", + "L\u03b26", + "Lβ6", + "\\ensuremath{\\mathrm{L}\\beta_6}", + ) assert base.Lb7.source_subshell == base.O1 assert base.Lb7.destination_subshell == base.L3 - yield XrayTransitionNotation(JENKINS1991, base.Lb7, base.SIEGBAHN, - 'Lb7', - 'L\u03b27', - 'Lβ7', - '\\ensuremath{\\mathrm{L}\\beta_7}') + yield XrayTransitionNotation( + JENKINS1991, + base.Lb7, + base.SIEGBAHN, + "Lb7", + "L\u03b27", + "Lβ7", + "\\ensuremath{\\mathrm{L}\\beta_7}", + ) assert base.Lb9.source_subshell == base.M5 assert base.Lb9.destination_subshell == base.L1 - yield XrayTransitionNotation(JENKINS1991, base.Lb9, base.SIEGBAHN, - 'Lb9', - 'L\u03b29', - 'Lβ9', - '\\ensuremath{\\mathrm{L}\\beta_9}') + yield XrayTransitionNotation( + JENKINS1991, + base.Lb9, + base.SIEGBAHN, + "Lb9", + "L\u03b29", + "Lβ9", + "\\ensuremath{\\mathrm{L}\\beta_9}", + ) assert base.Lb10.source_subshell == base.M4 assert base.Lb10.destination_subshell == base.L1 - yield XrayTransitionNotation(JENKINS1991, base.Lb10, base.SIEGBAHN, - 'Lb10', - 'L\u03b210', - 'Lβ10', - '\\ensuremath{\\mathrm{L}\\beta_{10}}') + yield XrayTransitionNotation( + JENKINS1991, + base.Lb10, + base.SIEGBAHN, + "Lb10", + "L\u03b210", + "Lβ10", + "\\ensuremath{\\mathrm{L}\\beta_{10}}", + ) assert base.Lb15.source_subshell == base.N4 assert base.Lb15.destination_subshell == base.L3 - yield XrayTransitionNotation(JENKINS1991, base.Lb15, base.SIEGBAHN, - 'Lb15', - 'L\u03b215', - 'Lβ15', - '\\ensuremath{\\mathrm{L}\\beta_{15}}') + yield XrayTransitionNotation( + JENKINS1991, + base.Lb15, + base.SIEGBAHN, + "Lb15", + "L\u03b215", + "Lβ15", + "\\ensuremath{\\mathrm{L}\\beta_{15}}", + ) assert base.Lb2_15.destination_subshell == base.L3 - yield XrayTransitionNotation(base.UNATTRIBUTED, base.Lb2_15, base.SIEGBAHN, - 'Lb2,15', - 'L\u03b22,15', - 'Lβ2,15', - '\\ensuremath{\\mathrm{L}\\beta_{2,15}}') - yield XrayTransitionNotation(base.UNATTRIBUTED, base.Lb2_15, base.IUPAC, - 'L3-N(4,5)', - 'L3\u2013N(4,5)', - 'L3–N4,5', - '\\ensuremath{\\mathrm{L}_3}--\\ensuremath{\\mathrm{N}_{4,5}}') + yield XrayTransitionNotation( + base.UNATTRIBUTED, + base.Lb2_15, + base.SIEGBAHN, + "Lb2,15", + "L\u03b22,15", + "Lβ2,15", + "\\ensuremath{\\mathrm{L}\\beta_{2,15}}", + ) + yield XrayTransitionNotation( + base.UNATTRIBUTED, + base.Lb2_15, + base.IUPAC, + "L3-N(4,5)", + "L3\u2013N(4,5)", + "L3–N4,5", + "\\ensuremath{\\mathrm{L}_3}--\\ensuremath{\\mathrm{N}_{4,5}}", + ) assert base.Lb17.source_subshell == base.M3 assert base.Lb17.destination_subshell == base.L2 - yield XrayTransitionNotation(JENKINS1991, base.Lb17, base.SIEGBAHN, - 'Lb17', - 'L\u03b217', - 'Lβ17', - '\\ensuremath{\\mathrm{L}\\beta_{17}}') + yield XrayTransitionNotation( + JENKINS1991, + base.Lb17, + base.SIEGBAHN, + "Lb17", + "L\u03b217", + "Lβ17", + "\\ensuremath{\\mathrm{L}\\beta_{17}}", + ) assert base.Lg1.source_subshell == base.N4 assert base.Lg1.destination_subshell == base.L2 - yield XrayTransitionNotation(JENKINS1991, base.Lg1, base.SIEGBAHN, - 'Lg1', - 'L\u03b31', - 'Lγ1', - '\\ensuremath{\\mathrm{L}\\gamma_1}') + yield XrayTransitionNotation( + JENKINS1991, + base.Lg1, + base.SIEGBAHN, + "Lg1", + "L\u03b31", + "Lγ1", + "\\ensuremath{\\mathrm{L}\\gamma_1}", + ) assert base.Lg2.source_subshell == base.N2 assert base.Lg2.destination_subshell == base.L1 - yield XrayTransitionNotation(JENKINS1991, base.Lg2, base.SIEGBAHN, - 'Lg2', - 'L\u03b32', - 'Lγ2', - '\\ensuremath{\\mathrm{L}\\gamma_2}') + yield XrayTransitionNotation( + JENKINS1991, + base.Lg2, + base.SIEGBAHN, + "Lg2", + "L\u03b32", + "Lγ2", + "\\ensuremath{\\mathrm{L}\\gamma_2}", + ) assert base.Lg3.source_subshell == base.N3 assert base.Lg3.destination_subshell == base.L1 - yield XrayTransitionNotation(JENKINS1991, base.Lg3, base.SIEGBAHN, - 'Lg3', - 'L\u03b33', - 'Lγ3', - '\\ensuremath{\\mathrm{L}\\gamma_3}') + yield XrayTransitionNotation( + JENKINS1991, + base.Lg3, + base.SIEGBAHN, + "Lg3", + "L\u03b33", + "Lγ3", + "\\ensuremath{\\mathrm{L}\\gamma_3}", + ) assert base.Lg2_3.destination_subshell == base.L1 - yield XrayTransitionNotation(base.UNATTRIBUTED, base.Lg2_3, base.SIEGBAHN, - 'Lg2,3', - 'L\u03b32,3', - 'Lγ2,3', - '\\ensuremath{\\mathrm{L}\\gamma_{2,3}}') - yield XrayTransitionNotation(base.UNATTRIBUTED, base.Lg2_3, base.IUPAC, - 'L1-N(2,3)', - 'L1\u2013N(2,3)', - 'L1–N2,3', - '\\ensuremath{\\mathrm{L}_1}--\\ensuremath{\\mathrm{N}_{2,3}}') + yield XrayTransitionNotation( + base.UNATTRIBUTED, + base.Lg2_3, + base.SIEGBAHN, + "Lg2,3", + "L\u03b32,3", + "Lγ2,3", + "\\ensuremath{\\mathrm{L}\\gamma_{2,3}}", + ) + yield XrayTransitionNotation( + base.UNATTRIBUTED, + base.Lg2_3, + base.IUPAC, + "L1-N(2,3)", + "L1\u2013N(2,3)", + "L1–N2,3", + "\\ensuremath{\\mathrm{L}_1}--\\ensuremath{\\mathrm{N}_{2,3}}", + ) assert base.Lg4.source_subshell == base.O3 assert base.Lg4.destination_subshell == base.L1 - yield XrayTransitionNotation(JENKINS1991, base.Lg4, base.SIEGBAHN, - 'Lg4', - 'L\u03b34', - 'Lγ4', - '\\ensuremath{\\mathrm{L}\\gamma_4}') + yield XrayTransitionNotation( + JENKINS1991, + base.Lg4, + base.SIEGBAHN, + "Lg4", + "L\u03b34", + "Lγ4", + "\\ensuremath{\\mathrm{L}\\gamma_4}", + ) assert base.Lg4p.source_subshell == base.O2 assert base.Lg4p.destination_subshell == base.L1 - yield XrayTransitionNotation(JENKINS1991, base.Lg4p, base.SIEGBAHN, - "Lg4'", - "L\u03b34\u2032", - 'Lγ4′', - '\\ensuremath{\\mathrm{L}\\gamma_{4\\prime}}') + yield XrayTransitionNotation( + JENKINS1991, + base.Lg4p, + base.SIEGBAHN, + "Lg4'", + "L\u03b34\u2032", + "Lγ4′", + "\\ensuremath{\\mathrm{L}\\gamma_{4\\prime}}", + ) assert base.Lg5.source_subshell == base.N1 assert base.Lg5.destination_subshell == base.L2 - yield XrayTransitionNotation(JENKINS1991, base.Lg5, base.SIEGBAHN, - 'Lg5', - 'L\u03b35', - 'Lγ5', - '\\ensuremath{\\mathrm{L}\\gamma_5}') + yield XrayTransitionNotation( + JENKINS1991, + base.Lg5, + base.SIEGBAHN, + "Lg5", + "L\u03b35", + "Lγ5", + "\\ensuremath{\\mathrm{L}\\gamma_5}", + ) assert base.Lg6.source_subshell == base.O4 assert base.Lg6.destination_subshell == base.L2 - yield XrayTransitionNotation(JENKINS1991, base.Lg6, base.SIEGBAHN, - 'Lg6', - 'L\u03b36', - 'Lγ6', - '\\ensuremath{\\mathrm{L}\\gamma_6}') + yield XrayTransitionNotation( + JENKINS1991, + base.Lg6, + base.SIEGBAHN, + "Lg6", + "L\u03b36", + "Lγ6", + "\\ensuremath{\\mathrm{L}\\gamma_6}", + ) assert base.Lg8.source_subshell == base.O1 assert base.Lg8.destination_subshell == base.L2 - yield XrayTransitionNotation(JENKINS1991, base.Lg8, base.SIEGBAHN, - 'Lg8', - 'L\u03b38', - 'Lγ8', - '\\ensuremath{\\mathrm{L}\\gamma_8}') + yield XrayTransitionNotation( + JENKINS1991, + base.Lg8, + base.SIEGBAHN, + "Lg8", + "L\u03b38", + "Lγ8", + "\\ensuremath{\\mathrm{L}\\gamma_8}", + ) assert base.Lg11.source_subshell == base.N5 assert base.Lg11.destination_subshell == base.L1 - yield XrayTransitionNotation(BEARDEN1967, base.Lg11, base.SIEGBAHN, - 'Lg11', - 'L\u03b311', - 'Lγ11', - '\\ensuremath{\\mathrm{L}\\gamma_11}') + yield XrayTransitionNotation( + BEARDEN1967, + base.Lg11, + base.SIEGBAHN, + "Lg11", + "L\u03b311", + "Lγ11", + "\\ensuremath{\\mathrm{L}\\gamma_11}", + ) assert base.Ln.source_subshell == base.M1 assert base.Ln.destination_subshell == base.L2 - yield XrayTransitionNotation(JENKINS1991, base.Ln, base.SIEGBAHN, - 'Ln', - 'L\u03b7', - 'Lη', - '\\ensuremath{\\mathrm{L}\\eta}') + yield XrayTransitionNotation( + JENKINS1991, + base.Ln, + base.SIEGBAHN, + "Ln", + "L\u03b7", + "Lη", + "\\ensuremath{\\mathrm{L}\\eta}", + ) assert base.Ll.source_subshell == base.M1 assert base.Ll.destination_subshell == base.L3 - yield XrayTransitionNotation(JENKINS1991, base.Ll, base.SIEGBAHN, - 'Ll', - 'L\u2113', - 'Lℓ', - '\\ensuremath{\\mathrm{L}\\ell}') + yield XrayTransitionNotation( + JENKINS1991, + base.Ll, + base.SIEGBAHN, + "Ll", + "L\u2113", + "Lℓ", + "\\ensuremath{\\mathrm{L}\\ell}", + ) assert base.Ll_n.source_subshell == base.M1 - yield XrayTransitionNotation(base.UNATTRIBUTED, base.Ll_n, base.SIEGBAHN, - 'Ll,n', - 'L\u2113,\u03b7', - 'Lℓ,η', - '\\ensuremath{\\mathrm{L}\\ell,\\eta}') - yield XrayTransitionNotation(base.UNATTRIBUTED, base.Ll_n, base.IUPAC, - 'L(2,3)-M1', - 'L(2,3)\u2013M1', - 'L2,3–M1', - '\\ensuremath{\\mathrm{L}_{2,3}}--\\ensuremath{\\mathrm{M}_1}') + yield XrayTransitionNotation( + base.UNATTRIBUTED, + base.Ll_n, + base.SIEGBAHN, + "Ll,n", + "L\u2113,\u03b7", + "Lℓ,η", + "\\ensuremath{\\mathrm{L}\\ell,\\eta}", + ) + yield XrayTransitionNotation( + base.UNATTRIBUTED, + base.Ll_n, + base.IUPAC, + "L(2,3)-M1", + "L(2,3)\u2013M1", + "L2,3–M1", + "\\ensuremath{\\mathrm{L}_{2,3}}--\\ensuremath{\\mathrm{M}_1}", + ) assert base.Ls.source_subshell == base.M3 assert base.Ls.destination_subshell == base.L3 - yield XrayTransitionNotation(JENKINS1991, base.Ls, base.SIEGBAHN, - 'Ls', - 'Ls', - 'Ls', - '\\ensuremath{\\mathrm{L}s}') + yield XrayTransitionNotation( + JENKINS1991, + base.Ls, + base.SIEGBAHN, + "Ls", + "Ls", + "Ls", + "\\ensuremath{\\mathrm{L}s}", + ) assert base.Lt.source_subshell == base.M2 assert base.Lt.destination_subshell == base.L3 - yield XrayTransitionNotation(JENKINS1991, base.Lt, base.SIEGBAHN, - 'Lt', - 'Lt', - 'Lt', - '\\ensuremath{\\mathrm{L}t}') + yield XrayTransitionNotation( + JENKINS1991, + base.Lt, + base.SIEGBAHN, + "Lt", + "Lt", + "Lt", + "\\ensuremath{\\mathrm{L}t}", + ) assert base.Lu_1.source_subshell == base.N7 assert base.Lu_1.destination_subshell == base.L3 - yield XrayTransitionNotation(BEARDEN1967, base.Lu_1, base.SIEGBAHN, - 'LuI', - 'LuI', - 'LuI', - '\\ensuremath{\\mathrm{L}u^I}') + yield XrayTransitionNotation( + BEARDEN1967, + base.Lu_1, + base.SIEGBAHN, + "LuI", + "LuI", + "LuI", + "\\ensuremath{\\mathrm{L}u^I}", + ) assert base.Lu_2.source_subshell == base.N6 assert base.Lu_2.destination_subshell == base.L3 - yield XrayTransitionNotation(BEARDEN1967, base.Lu_2, base.SIEGBAHN, - 'LuII', - 'LuII', - 'LuII', - '\\ensuremath{\\mathrm{L}u^{II}}') + yield XrayTransitionNotation( + BEARDEN1967, + base.Lu_2, + base.SIEGBAHN, + "LuII", + "LuII", + "LuII", + "\\ensuremath{\\mathrm{L}u^{II}}", + ) assert base.Lu.destination_subshell == base.L3 - yield XrayTransitionNotation(BEARDEN1967, base.Lu, base.SIEGBAHN, - 'Lu', - 'Lu', - 'Lu', - '\\ensuremath{\\mathrm{L}u}') + yield XrayTransitionNotation( + BEARDEN1967, + base.Lu, + base.SIEGBAHN, + "Lu", + "Lu", + "Lu", + "\\ensuremath{\\mathrm{L}u}", + ) assert base.Lv.source_subshell == base.N6 assert base.Lv.destination_subshell == base.L2 - yield XrayTransitionNotation(JENKINS1991, base.Lv, base.SIEGBAHN, - 'Lv', - 'L\u03bd', - 'Lν', - '\\ensuremath{\\mathrm{L}\\nu}') + yield XrayTransitionNotation( + JENKINS1991, + base.Lv, + base.SIEGBAHN, + "Lv", + "L\u03bd", + "Lν", + "\\ensuremath{\\mathrm{L}\\nu}", + ) assert base.Ma1.source_subshell == base.N7 assert base.Ma1.destination_subshell == base.M5 - yield XrayTransitionNotation(JENKINS1991, base.Ma1, base.SIEGBAHN, - 'Ma1', - 'M\u03b11', - 'Mα1', - '\\ensuremath{\\mathrm{M}\\alpha_1}') + yield XrayTransitionNotation( + JENKINS1991, + base.Ma1, + base.SIEGBAHN, + "Ma1", + "M\u03b11", + "Mα1", + "\\ensuremath{\\mathrm{M}\\alpha_1}", + ) assert base.Ma2.source_subshell == base.N6 assert base.Ma2.destination_subshell == base.M5 - yield XrayTransitionNotation(JENKINS1991, base.Ma2, base.SIEGBAHN, - 'Ma2', - 'M\u03b12', - 'Mα2', - '\\ensuremath{\\mathrm{M}\\alpha_2}') + yield XrayTransitionNotation( + JENKINS1991, + base.Ma2, + base.SIEGBAHN, + "Ma2", + "M\u03b12", + "Mα2", + "\\ensuremath{\\mathrm{M}\\alpha_2}", + ) assert base.Ma.destination_subshell == base.M5 - yield XrayTransitionNotation(JENKINS1991, base.Ma, base.SIEGBAHN, - 'Ma', - 'M\u03b1', - 'Mα', - '\\ensuremath{\\mathrm{M}\\alpha}') - yield XrayTransitionNotation(JENKINS1991, base.Ma, base.IUPAC, - 'M5-N(6,7)', - 'M5\u2013N(6,7)', - 'M5–N6,7', - '\\ensuremath{\\mathrm{M}_5}--\\ensuremath{\\mathrm{N}_{6,7}}') + yield XrayTransitionNotation( + JENKINS1991, + base.Ma, + base.SIEGBAHN, + "Ma", + "M\u03b1", + "Mα", + "\\ensuremath{\\mathrm{M}\\alpha}", + ) + yield XrayTransitionNotation( + JENKINS1991, + base.Ma, + base.IUPAC, + "M5-N(6,7)", + "M5\u2013N(6,7)", + "M5–N6,7", + "\\ensuremath{\\mathrm{M}_5}--\\ensuremath{\\mathrm{N}_{6,7}}", + ) assert base.Mb.source_subshell == base.N6 assert base.Mb.destination_subshell == base.M4 - yield XrayTransitionNotation(JENKINS1991, base.Mb, base.SIEGBAHN, - 'Mb', - 'M\u03b2', - 'Mβ', - '\\ensuremath{\\mathrm{M}\\beta}') + yield XrayTransitionNotation( + JENKINS1991, + base.Mb, + base.SIEGBAHN, + "Mb", + "M\u03b2", + "Mβ", + "\\ensuremath{\\mathrm{M}\\beta}", + ) assert base.Mg.source_subshell == base.N5 assert base.Mg.destination_subshell == base.M3 - yield XrayTransitionNotation(JENKINS1991, base.Mg, base.SIEGBAHN, - 'Mg', - 'M\u03b3', - 'Mγ', - '\\ensuremath{\\mathrm{M}\\gamma}') + yield XrayTransitionNotation( + JENKINS1991, + base.Mg, + base.SIEGBAHN, + "Mg", + "M\u03b3", + "Mγ", + "\\ensuremath{\\mathrm{M}\\gamma}", + ) assert base.Mz1.source_subshell == base.N3 assert base.Mz1.destination_subshell == base.M5 - yield XrayTransitionNotation(BEARDEN1967, base.Mz1, base.SIEGBAHN, - 'Mz1', - 'M\u03b61', - 'Mζ1', - '\\ensuremath{\\mathrm{M}\\zeta_1}') + yield XrayTransitionNotation( + BEARDEN1967, + base.Mz1, + base.SIEGBAHN, + "Mz1", + "M\u03b61", + "Mζ1", + "\\ensuremath{\\mathrm{M}\\zeta_1}", + ) assert base.Mz2.source_subshell == base.N2 assert base.Mz2.destination_subshell == base.M4 - yield XrayTransitionNotation(BEARDEN1967, base.Mz2, base.SIEGBAHN, - 'Mz2', - 'M\u03b62', - 'Mζ2', - '\\ensuremath{\\mathrm{M}\\zeta_2}') - - yield XrayTransitionNotation(BEARDEN1967, base.Mz, base.SIEGBAHN, - 'Mz', - 'M\u03b6', - 'Mζ', - '\\ensuremath{\\mathrm{M}\\zeta}') - yield XrayTransitionNotation(BEARDEN1967, base.Mz, base.IUPAC, - 'M(4,5)-N(2,3)', - 'M(4,5)\u2013N(2,3)', - 'M4,5–N2,3', - '\\ensuremath{\\mathrm{M}_{4,5}}--\\ensuremath{\\mathrm{N}_{2,3}}') + yield XrayTransitionNotation( + BEARDEN1967, + base.Mz2, + base.SIEGBAHN, + "Mz2", + "M\u03b62", + "Mζ2", + "\\ensuremath{\\mathrm{M}\\zeta_2}", + ) + + yield XrayTransitionNotation( + BEARDEN1967, + base.Mz, + base.SIEGBAHN, + "Mz", + "M\u03b6", + "Mζ", + "\\ensuremath{\\mathrm{M}\\zeta}", + ) + yield XrayTransitionNotation( + BEARDEN1967, + base.Mz, + base.IUPAC, + "M(4,5)-N(2,3)", + "M(4,5)\u2013N(2,3)", + "M4,5–N2,3", + "\\ensuremath{\\mathrm{M}_{4,5}}--\\ensuremath{\\mathrm{N}_{2,3}}", + ) assert base.M1N2_3.destination_subshell == base.M1 - yield XrayTransitionNotation(base.UNATTRIBUTED, base.M1N2_3, base.IUPAC, - 'M1-N(2,3)', - 'M1\u2013N(2,3)', - 'M1–N2,3', - '\\ensuremath{\\mathrm{M}_1}--\\ensuremath{\\mathrm{N}_{2,3}}') - - yield XrayTransitionNotation(base.UNATTRIBUTED, base.M2_3M4_5, base.IUPAC, - 'M(2,3)-M(4,5)', - 'M(2,3)\u2013M(4,5)', - 'M2,3–M4,5', - '\\ensuremath{\\mathrm{M}_{2,3}}--\\ensuremath{\\mathrm{M}_{4,5}}') - - yield XrayTransitionNotation(base.UNATTRIBUTED, base.M4_5O2_3, base.IUPAC, - 'M(4,5)-O(2,3)', - 'M(4,5)\u2013O(2,3)', - 'M4,5–O2,3', - '\\ensuremath{\\mathrm{M}_{4,5}}--\\ensuremath{\\mathrm{O}_{2,3}}') - - yield XrayTransitionNotation(base.UNATTRIBUTED, base.M3O4_5, base.IUPAC, - 'M3-O(4,5)', - 'M3\u2013O(4,5)', - 'M3–O4,5', - '\\ensuremath{\\mathrm{M}_3}--\\ensuremath{\\mathrm{O}_{4,5}}') - - yield XrayTransitionNotation(base.UNATTRIBUTED, base.M4O2_3, base.IUPAC, - 'M4-O(2,3)', - 'M4\u2013O(2,3)', - 'M4–O2,3', - '\\ensuremath{\\mathrm{M}_4}--\\ensuremath{\\mathrm{O}_{2,3}}') + yield XrayTransitionNotation( + base.UNATTRIBUTED, + base.M1N2_3, + base.IUPAC, + "M1-N(2,3)", + "M1\u2013N(2,3)", + "M1–N2,3", + "\\ensuremath{\\mathrm{M}_1}--\\ensuremath{\\mathrm{N}_{2,3}}", + ) + + yield XrayTransitionNotation( + base.UNATTRIBUTED, + base.M2_3M4_5, + base.IUPAC, + "M(2,3)-M(4,5)", + "M(2,3)\u2013M(4,5)", + "M2,3–M4,5", + "\\ensuremath{\\mathrm{M}_{2,3}}--\\ensuremath{\\mathrm{M}_{4,5}}", + ) + + yield XrayTransitionNotation( + base.UNATTRIBUTED, + base.M4_5O2_3, + base.IUPAC, + "M(4,5)-O(2,3)", + "M(4,5)\u2013O(2,3)", + "M4,5–O2,3", + "\\ensuremath{\\mathrm{M}_{4,5}}--\\ensuremath{\\mathrm{O}_{2,3}}", + ) + + yield XrayTransitionNotation( + base.UNATTRIBUTED, + base.M3O4_5, + base.IUPAC, + "M3-O(4,5)", + "M3\u2013O(4,5)", + "M3–O4,5", + "\\ensuremath{\\mathrm{M}_3}--\\ensuremath{\\mathrm{O}_{4,5}}", + ) + + yield XrayTransitionNotation( + base.UNATTRIBUTED, + base.M4O2_3, + base.IUPAC, + "M4-O(2,3)", + "M4\u2013O(2,3)", + "M4–O2,3", + "\\ensuremath{\\mathrm{M}_4}--\\ensuremath{\\mathrm{O}_{2,3}}", + ) -class SeriesXrayTransitionNotationParser(base._Parser): +class SeriesXrayTransitionNotationParser(base._Parser): def __iter__(self): for n in range(1, MAX_N + 1): - transition = XrayTransition(None, None, None, n, None, None) # All transitions to this shell - - ascii, utf16, html, latex = AtomicShellNotationParser._create_entry_siegbahn(n) - yield XrayTransitionNotation(base.UNATTRIBUTED, transition, base.SIEGBAHN, - ascii, utf16, html, latex) + transition = XrayTransition( + None, None, None, n, None, None + ) # All transitions to this shell + + ( + ascii, + utf16, + html, + latex, + ) = AtomicShellNotationParser._create_entry_siegbahn(n) + yield XrayTransitionNotation( + base.UNATTRIBUTED, transition, base.SIEGBAHN, ascii, utf16, html, latex + ) ascii, utf16, html, latex = AtomicShellNotationParser._create_entry_iupac(n) - yield XrayTransitionNotation(base.UNATTRIBUTED, transition, base.IUPAC, - ascii, utf16, html, latex) + yield XrayTransitionNotation( + base.UNATTRIBUTED, transition, base.IUPAC, ascii, utf16, html, latex + ) -class FamilyXrayTransitionNotationParser(base._Parser): +class FamilyXrayTransitionNotationParser(base._Parser): def __iter__(self): families = set() for transition, _src_i, dst_i in iter_transitions(MAX_N): @@ -823,19 +1302,33 @@ def __iter__(self): # Create family notations for atomic_subshell, i in families: - if i == 0: # Skip K, already a series + if i == 0: # Skip K, already a series continue n = atomic_subshell.n l = atomic_subshell.l j_n = atomic_subshell.j_n - transition = XrayTransition(None, None, None, n, l, j_n) # All transitions to this subshell - - ascii, utf16, html, latex = AtomicSubshellNotationParser._create_entry_siegbahn(n, l, j_n, i) - yield XrayTransitionNotation(base.UNATTRIBUTED, transition, base.SIEGBAHN, - ascii, utf16, html, latex) - - ascii, utf16, html, latex = AtomicSubshellNotationParser._create_entry_iupac(n, l, j_n, i) - yield XrayTransitionNotation(base.UNATTRIBUTED, transition, base.IUPAC, - ascii, utf16, html, latex) + transition = XrayTransition( + None, None, None, n, l, j_n + ) # All transitions to this subshell + + ( + ascii, + utf16, + html, + latex, + ) = AtomicSubshellNotationParser._create_entry_siegbahn(n, l, j_n, i) + yield XrayTransitionNotation( + base.UNATTRIBUTED, transition, base.SIEGBAHN, ascii, utf16, html, latex + ) + + ( + ascii, + utf16, + html, + latex, + ) = AtomicSubshellNotationParser._create_entry_iupac(n, l, j_n, i) + yield XrayTransitionNotation( + base.UNATTRIBUTED, transition, base.IUPAC, ascii, utf16, html, latex + ) diff --git a/pyxray/parser/perkins1991.py b/pyxray/parser/perkins1991.py index e423106..300ce4a 100644 --- a/pyxray/parser/perkins1991.py +++ b/pyxray/parser/perkins1991.py @@ -13,52 +13,56 @@ # Local modules. from pyxray.descriptor import Reference, Element, AtomicSubshell, XrayTransition -from pyxray.property import \ - (ElementAtomicWeight, - AtomicSubshellBindingEnergy, - AtomicSubshellRadiativeWidth, AtomicSubshellNonRadiativeWidth, - AtomicSubshellOccupancy, - XrayTransitionEnergy, XrayTransitionProbability) +from pyxray.property import ( + ElementAtomicWeight, + AtomicSubshellBindingEnergy, + AtomicSubshellRadiativeWidth, + AtomicSubshellNonRadiativeWidth, + AtomicSubshellOccupancy, + XrayTransitionEnergy, + XrayTransitionProbability, +) import pyxray.parser.base as base # Globals and constants variables. logger = logging.getLogger(__name__) -PERKINS1991 = Reference('perkins1991', - author='Perkins, S.T. and Cullen, D.E.', - title='Tables and Graphs of Atomic Subshell and Relaxation Data Derived from the LLNL Evaluated Atomic Data Library (EADL), Z = 1 - 100', - organization='Lawrence Livermore National Laboratory', - year=1991, - volume=30) +PERKINS1991 = Reference( + "perkins1991", + author="Perkins, S.T. and Cullen, D.E.", + title="Tables and Graphs of Atomic Subshell and Relaxation Data Derived from the LLNL Evaluated Atomic Data Library (EADL), Z = 1 - 100", + organization="Lawrence Livermore National Laboratory", + year=1991, + volume=30, +) -EADL_URL = 'https://www-nds.iaea.org/epdl97/data/endl/eadl/eadl.all' +EADL_URL = "https://www-nds.iaea.org/epdl97/data/endl/eadl/eadl.all" + +FLOAT_PATTERN = re.compile(r"([\d.]+)([\+\-])([\d ]+)") -FLOAT_PATTERN = re.compile(r'([\d.]+)([\+\-])([\d ]+)') def float_(text): text = text.strip() base, sign, exp = FLOAT_PATTERN.match(text).groups() base = float(base) - exp = float(exp) if sign == '+' else -float(exp) + exp = float(exp) if sign == "+" else -float(exp) return base * 10 ** exp + # Conversion from EADL to AtomicSubshell descriptor ATOMIC_SUBSHELLS = { # K 1: base.K, - # L 3: base.L1, 5: base.L2, 6: base.L3, - # M 8: base.M1, 10: base.M2, 11: base.M3, 13: base.M4, 14: base.M5, - # N 16: base.N1, 18: base.N2, @@ -67,7 +71,6 @@ def float_(text): 22: base.N5, 24: base.N6, 25: base.N7, - # O 27: base.O1, 29: base.O2, @@ -78,7 +81,6 @@ def float_(text): 36: base.O7, 38: base.O8, 39: base.O9, - # P 41: base.P1, 43: base.P2, @@ -91,12 +93,11 @@ def float_(text): 53: base.P9, 55: base.P10, 56: base.P11, - # Q 58: base.Q1, 60: base.Q2, 61: base.Q3, - } +} # C REACTION_DESCRIPTOR_SUBSHELL = 91 @@ -119,19 +120,19 @@ def float_(text): OUTGOING_PARTICLE_PHOTON = 7 OUTGOING_PARTICLE_ELECTRON = 9 -SEPERATOR = ' 1' +SEPERATOR = " 1" MAX_Z = 100 -class Perkins1991Parser(base._Parser): +class Perkins1991Parser(base._Parser): def __iter__(self): r = requests.get(EADL_URL, stream=True, verify=False) try: rows = [] for line in r.iter_lines(): - line = line.decode('ascii') + line = line.decode("ascii") if line == SEPERATOR: yield from self._iter_rows(rows) @@ -151,10 +152,11 @@ def _iter_rows(self, rows): yield from self._parse_atomic_weight(rows) yield from self._parse_atomic_subshell_binding_energy(rows) yield from self._parse_atomic_subshell_radiative_width(rows) -# yield from self._parse_atomic_subshell_nonradiative_width(rows) + # yield from self._parse_atomic_subshell_nonradiative_width(rows) yield from self._parse_atomic_subshell_occupaqncy(rows) yield from self._parse_radiative_transition(rows) -# yield from self._parse_nonradiative_transition(rows) + + # yield from self._parse_nonradiative_transition(rows) def _extract_element(self, rows): return Element(int(rows[0][0:3])) @@ -194,7 +196,7 @@ def _parse_atomic_weight(self, rows): element = self._extract_element(rows) value = float_(rows[0][13:24]) prop = ElementAtomicWeight(PERKINS1991, element, value) - logger.debug('Parsed: {0}'.format(prop)) + logger.debug("Parsed: {0}".format(prop)) yield prop def _parse_atomic_subshell_binding_energy(self, rows): @@ -219,9 +221,10 @@ def _parse_atomic_subshell_binding_energy(self, rows): for row in rows[2:]: atomic_subshell = self._extract_source_subshell([row]) value_eV = float_(row[11:22]) * 1e6 - prop = AtomicSubshellBindingEnergy(PERKINS1991, element, - atomic_subshell, value_eV) - logger.debug('Parsed: {0}'.format(prop)) + prop = AtomicSubshellBindingEnergy( + PERKINS1991, element, atomic_subshell, value_eV + ) + logger.debug("Parsed: {0}".format(prop)) yield prop def _parse_atomic_subshell_radiative_width(self, rows): @@ -246,9 +249,10 @@ def _parse_atomic_subshell_radiative_width(self, rows): for row in rows[2:]: atomic_subshell = self._extract_source_subshell([row]) value_eV = float_(row[11:22]) * 1e6 - prop = AtomicSubshellRadiativeWidth(PERKINS1991, element, - atomic_subshell, value_eV) - logger.debug('Parsed: {0}'.format(prop)) + prop = AtomicSubshellRadiativeWidth( + PERKINS1991, element, atomic_subshell, value_eV + ) + logger.debug("Parsed: {0}".format(prop)) yield prop def _parse_atomic_subshell_nonradiative_width(self, rows): @@ -273,9 +277,10 @@ def _parse_atomic_subshell_nonradiative_width(self, rows): for row in rows[2:]: atomic_subshell = self._extract_source_subshell([row]) value_eV = float_(row[11:22]) * 1e6 - prop = AtomicSubshellNonRadiativeWidth(PERKINS1991, element, - atomic_subshell, value_eV) - logger.debug('Parsed: {0}'.format(prop)) + prop = AtomicSubshellNonRadiativeWidth( + PERKINS1991, element, atomic_subshell, value_eV + ) + logger.debug("Parsed: {0}".format(prop)) yield prop def _parse_atomic_subshell_occupaqncy(self, rows): @@ -300,9 +305,8 @@ def _parse_atomic_subshell_occupaqncy(self, rows): for row in rows[2:]: atomic_subshell = self._extract_source_subshell([row]) value = int(float_(row[11:22])) - prop = AtomicSubshellOccupancy(PERKINS1991, element, - atomic_subshell, value) - logger.debug('Parsed: {0}'.format(prop)) + prop = AtomicSubshellOccupancy(PERKINS1991, element, atomic_subshell, value) + logger.debug("Parsed: {0}".format(prop)) yield prop def _parse_radiative_transition(self, rows): @@ -332,14 +336,15 @@ def _parse_radiative_transition(self, rows): value = float_(row[11:22]) prop = XrayTransitionProbability(PERKINS1991, element, transition, value) - logger.debug('Parsed: {0}'.format(prop)) + logger.debug("Parsed: {0}".format(prop)) yield prop value_eV = float_(row[22:33]) * 1e6 prop = XrayTransitionEnergy(PERKINS1991, element, transition, value_eV) - logger.debug('Parsed: {0}'.format(prop)) + logger.debug("Parsed: {0}".format(prop)) yield prop + # def _parse_nonradiative_transition(self, rows): # reaction_descriptor = self._extract_reaction_descriptor(rows) # if reaction_descriptor != REACTION_DESCRIPTOR_TRANSITION: diff --git a/pyxray/parser/sargent_welch.py b/pyxray/parser/sargent_welch.py index 0807d2b..4f21e9d 100644 --- a/pyxray/parser/sargent_welch.py +++ b/pyxray/parser/sargent_welch.py @@ -4,6 +4,7 @@ # Standard library modules. import logging + logger = logging.getLogger(__name__) # Third party modules. @@ -15,27 +16,114 @@ # Globals and constants variables. -SARGENT_WELCH = Reference('sargentwelch2010', - year=2010, - title='Student Periodic Tables', - publisher='Sargent-Welch scientifique Canada Limitee', - note='Extracted by Hendrix Demers') +SARGENT_WELCH = Reference( + "sargentwelch2010", + year=2010, + title="Student Periodic Tables", + publisher="Sargent-Welch scientifique Canada Limitee", + note="Extracted by Hendrix Demers", +) + class SargentWelchElementMassDensityParser(base._Parser): DENSITIES = [ - 0.0899, 0.1787, 0.5300, 1.8500, 2.3400, 2.6200, 1.2510, 1.4290, - 1.6960, 0.9010, 0.9700, 1.7400, 2.7000, 2.3300, 1.8200, 2.0700, - 3.1700, 1.7840, 0.8600, 1.5500, 3.0000, 4.5000, 5.8000, 7.1900, - 7.4300, 7.8600, 8.9000, 8.9000, 8.9600, 7.1400, 5.9100, 5.3200, - 5.7200, 4.8000, 3.1200, 3.7400, 1.5300, 2.6000, 4.5000, 6.4900, - 8.5500, 10.200, 11.500, 12.200, 12.400, 12.000, 10.500, 8.6500, - 7.3100, 7.3000, 6.6800, 6.2400, 4.9200, 5.8900, 1.8700, 3.5000, - 6.7000, 6.7800, 6.7700, 7.0000, 6.4750, 7.5400, 5.2600, 7.8900, - 8.2700, 8.5400, 8.8000, 9.0500, 9.3300, 6.9800, 9.8400, 13.100, - 16.600, 19.300, 21.000, 22.400, 22.500, 21.400, 19.300, 13.530, - 11.850, 11.400, 9.8000, 9.4000, None , 9.9100, None , 5.0000, - 10.070, 11.700, 15.400, 18.900, 20.400, 19.800, 13.600, 13.511 + 0.0899, + 0.1787, + 0.5300, + 1.8500, + 2.3400, + 2.6200, + 1.2510, + 1.4290, + 1.6960, + 0.9010, + 0.9700, + 1.7400, + 2.7000, + 2.3300, + 1.8200, + 2.0700, + 3.1700, + 1.7840, + 0.8600, + 1.5500, + 3.0000, + 4.5000, + 5.8000, + 7.1900, + 7.4300, + 7.8600, + 8.9000, + 8.9000, + 8.9600, + 7.1400, + 5.9100, + 5.3200, + 5.7200, + 4.8000, + 3.1200, + 3.7400, + 1.5300, + 2.6000, + 4.5000, + 6.4900, + 8.5500, + 10.200, + 11.500, + 12.200, + 12.400, + 12.000, + 10.500, + 8.6500, + 7.3100, + 7.3000, + 6.6800, + 6.2400, + 4.9200, + 5.8900, + 1.8700, + 3.5000, + 6.7000, + 6.7800, + 6.7700, + 7.0000, + 6.4750, + 7.5400, + 5.2600, + 7.8900, + 8.2700, + 8.5400, + 8.8000, + 9.0500, + 9.3300, + 6.9800, + 9.8400, + 13.100, + 16.600, + 19.300, + 21.000, + 22.400, + 22.500, + 21.400, + 19.300, + 13.530, + 11.850, + 11.400, + 9.8000, + 9.4000, + None, + 9.9100, + None, + 5.0000, + 10.070, + 11.700, + 15.400, + 18.900, + 20.400, + 19.800, + 13.600, + 13.511, ] def __iter__(self): @@ -45,31 +133,120 @@ def __iter__(self): continue element = Element(z) prop = ElementMassDensity(SARGENT_WELCH, element, rho * 1000.0) - logger.debug('Parsed: {0}'.format(prop)) + logger.debug("Parsed: {0}".format(prop)) self.update(int((z - 1) / length * 100.0)) yield prop + class SargentWelchElementAtomicWeightParser(base._Parser): ATOMIC_WEIGHTS = [ - 1.0079000, 4.0026000, 6.9410000, 9.0121800, 10.810000, 12.011000, - 14.006700, 15.999400, 18.998403, 20.179000, 22.989770, 24.305000, - 26.981540, 28.085500, 30.973760, 32.060000, 35.453000, 39.948000, - 39.098300, 40.080000, 44.955900, 47.900000, 50.941500, 51.996000, - 54.938000, 55.847000, 58.933200, 58.700000, 63.546000, 65.380000, - 69.720000, 72.590000, 74.921600, 78.960000, 79.904000, 83.800000, - 85.467800, 87.620000, 88.905600, 91.220000, 92.906400, 95.940000, - 98.000000, 101.07000, 102.90550, 106.40000, 107.86800, 112.41000, - 114.82000, 118.69000, 121.75000, 127.60000, 126.90450, 131.30000, - 132.90540, 137.33000, 138.90550, 140.12000, 140.90770, 144.24000, - 145.00000, 150.40000, 151.96000, 157.25000, 158.92540, 162.50000, - 164.93040, 167.26000, 168.93420, 173.04000, 174.96700, 178.49000, - 180.94790, 183.85000, 186.20700, 190.20000, 192.22000, 195.09000, - 196.96650, 200.59000, 204.37000, 207.20000, 208.98040, 209.00000, - 210.00000, 222.00000, 223.00000, 226.02540, 227.02780, 232.03810, - 231.03590, 238.02900, 237.04820, 244.00000, 243.00000, 247.00000, - 247.00000, 251.00000, 252.00000, 257.00000, 258.00000, 259.00000, - 260.00000, 261.00000, 262.00000, 263.00000 + 1.0079000, + 4.0026000, + 6.9410000, + 9.0121800, + 10.810000, + 12.011000, + 14.006700, + 15.999400, + 18.998403, + 20.179000, + 22.989770, + 24.305000, + 26.981540, + 28.085500, + 30.973760, + 32.060000, + 35.453000, + 39.948000, + 39.098300, + 40.080000, + 44.955900, + 47.900000, + 50.941500, + 51.996000, + 54.938000, + 55.847000, + 58.933200, + 58.700000, + 63.546000, + 65.380000, + 69.720000, + 72.590000, + 74.921600, + 78.960000, + 79.904000, + 83.800000, + 85.467800, + 87.620000, + 88.905600, + 91.220000, + 92.906400, + 95.940000, + 98.000000, + 101.07000, + 102.90550, + 106.40000, + 107.86800, + 112.41000, + 114.82000, + 118.69000, + 121.75000, + 127.60000, + 126.90450, + 131.30000, + 132.90540, + 137.33000, + 138.90550, + 140.12000, + 140.90770, + 144.24000, + 145.00000, + 150.40000, + 151.96000, + 157.25000, + 158.92540, + 162.50000, + 164.93040, + 167.26000, + 168.93420, + 173.04000, + 174.96700, + 178.49000, + 180.94790, + 183.85000, + 186.20700, + 190.20000, + 192.22000, + 195.09000, + 196.96650, + 200.59000, + 204.37000, + 207.20000, + 208.98040, + 209.00000, + 210.00000, + 222.00000, + 223.00000, + 226.02540, + 227.02780, + 232.03810, + 231.03590, + 238.02900, + 237.04820, + 244.00000, + 243.00000, + 247.00000, + 247.00000, + 251.00000, + 252.00000, + 257.00000, + 258.00000, + 259.00000, + 260.00000, + 261.00000, + 262.00000, + 263.00000, ] def __iter__(self): @@ -79,6 +256,6 @@ def __iter__(self): continue element = Element(z) prop = ElementAtomicWeight(SARGENT_WELCH, element, aw) - logger.debug('Parsed: {0}'.format(prop)) + logger.debug("Parsed: {0}".format(prop)) self.update(int((z - 1) / length * 100.0)) yield prop diff --git a/pyxray/parser/wikipedia.py b/pyxray/parser/wikipedia.py index 5322cd6..0d5fa89 100644 --- a/pyxray/parser/wikipedia.py +++ b/pyxray/parser/wikipedia.py @@ -5,6 +5,7 @@ # Standard library modules. import os import logging + logger = logging.getLogger(__name__) # Third party modules. @@ -17,49 +18,166 @@ # Globals and constants variables. -WIKIPEDIA = Reference('wikipedia', - author='Wikipedia contributors') +WIKIPEDIA = Reference("wikipedia", author="Wikipedia contributors") + class WikipediaElementNameParser(base._Parser): NAMES_EN = [ - "Hydrogen" , "Helium" , "Lithium" , "Beryllium" , - "Boron" , "Carbon" , "Nitrogen" , "Oxygen" , - "Fluorine" , "Neon" , "Sodium" , "Magnesium" , - "Aluminium" , "Silicon" , "Phosphorus" , "Sulfur" , - "Chlorine" , "Argon" , "Potassium" , "Calcium" , - "Scandium" , "Titanium" , "Vanadium" , "Chromium" , - "Manganese" , "Iron" , "Cobalt" , "Nickel" , - "Copper" , "Zinc" , "Gallium" , "Germanium" , - "Arsenic" , "Selenium" , "Bromine" , "Krypton" , - "Rubidium" , "Strontium" , "Yttrium" , "Zirconium" , - "Niobium" , "Molybdenum" , "Technetium" , "Ruthenium" , - "Rhodium" , "Palladium" , "Silver" , "Cadmium" , - "Indium" , "Tin" , "Antimony" , "Tellurium" , - "Iodine" , "Xenon" , "Cesium" , "Barium" , - "Lanthanum" , "Cerium" , "Praseodymium", "Neodymium" , - "Promethium" , "Samarium" , "Europium" , "Gadolinium" , - "Terbium" , "Dysprosium" , "Holmium" , "Erbium" , - "Thulium" , "Ytterbium" , "Lutetium" , "Hafnium" , - "Tantalum" , "Tungsten" , "Rhenium" , "Osmium" , - "Iridium" , "Platinum" , "Gold" , "Mercury" , - "Thallium" , "Lead" , "Bismuth" , "Polonium" , - "Astatine" , "Radon" , "Francium" , "Radium" , - "Actinium" , "Thorium" , "Protactinium", "Uranium" , - "Neptunium" , "Plutonium" , "Americium" , "Curium" , - "Berkelium" , "Californium" , "Einsteinium" , "Fermium" , - "Mendelevium" , "Nobelium" , "Lawrencium" , "Rutherfordium", - "Dubnium" , "Seaborgium" , "Bohrium" , "Hassium" , - "Meitnerium" , "Darmstadtium", "Roentgenium" , "Copernicium" , - "Ununtrium" , "Flerovium" , "Ununpentium" , "Livermorium" , - "Ununseptium" , "Ununoctium" + "Hydrogen", + "Helium", + "Lithium", + "Beryllium", + "Boron", + "Carbon", + "Nitrogen", + "Oxygen", + "Fluorine", + "Neon", + "Sodium", + "Magnesium", + "Aluminium", + "Silicon", + "Phosphorus", + "Sulfur", + "Chlorine", + "Argon", + "Potassium", + "Calcium", + "Scandium", + "Titanium", + "Vanadium", + "Chromium", + "Manganese", + "Iron", + "Cobalt", + "Nickel", + "Copper", + "Zinc", + "Gallium", + "Germanium", + "Arsenic", + "Selenium", + "Bromine", + "Krypton", + "Rubidium", + "Strontium", + "Yttrium", + "Zirconium", + "Niobium", + "Molybdenum", + "Technetium", + "Ruthenium", + "Rhodium", + "Palladium", + "Silver", + "Cadmium", + "Indium", + "Tin", + "Antimony", + "Tellurium", + "Iodine", + "Xenon", + "Cesium", + "Barium", + "Lanthanum", + "Cerium", + "Praseodymium", + "Neodymium", + "Promethium", + "Samarium", + "Europium", + "Gadolinium", + "Terbium", + "Dysprosium", + "Holmium", + "Erbium", + "Thulium", + "Ytterbium", + "Lutetium", + "Hafnium", + "Tantalum", + "Tungsten", + "Rhenium", + "Osmium", + "Iridium", + "Platinum", + "Gold", + "Mercury", + "Thallium", + "Lead", + "Bismuth", + "Polonium", + "Astatine", + "Radon", + "Francium", + "Radium", + "Actinium", + "Thorium", + "Protactinium", + "Uranium", + "Neptunium", + "Plutonium", + "Americium", + "Curium", + "Berkelium", + "Californium", + "Einsteinium", + "Fermium", + "Mendelevium", + "Nobelium", + "Lawrencium", + "Rutherfordium", + "Dubnium", + "Seaborgium", + "Bohrium", + "Hassium", + "Meitnerium", + "Darmstadtium", + "Roentgenium", + "Copernicium", + "Ununtrium", + "Flerovium", + "Ununpentium", + "Livermorium", + "Ununseptium", + "Ununoctium", ] # 30 most spoken languages in the world # https://en.wikipedia.org/wiki/List_of_languages_by_number_of_native_speakers - LANGUAGES = ['cmn', 'es', 'en', 'hi', 'ar', 'pt', 'bn', 'ru', 'ja', 'pa', - 'de', 'jv', 'wuu', 'ms', 'te', 'vi', 'ko', 'fr', 'mr', 'ta', - 'ur', 'tr', 'it', 'yue', 'th', 'gu', 'cjy', 'nan', 'fa', 'pl'] + LANGUAGES = [ + "cmn", + "es", + "en", + "hi", + "ar", + "pt", + "bn", + "ru", + "ja", + "pa", + "de", + "jv", + "wuu", + "ms", + "te", + "vi", + "ko", + "fr", + "mr", + "ta", + "ur", + "tr", + "it", + "yue", + "th", + "gu", + "cjy", + "nan", + "fa", + "pl", + ] def _find_wikipedia_names(self, name_en): """ @@ -67,22 +185,24 @@ def _find_wikipedia_names(self, name_en): returns a dictionary where the keys are the language code and the values are the titles of the corresponding pages. """ - url = 'https://en.wikipedia.org/w/api.php' - params = {'action': 'query', - 'titles': name_en, - 'prop': 'langlinks', - 'lllimit': 500, - 'format': 'json'} + url = "https://en.wikipedia.org/w/api.php" + params = { + "action": "query", + "titles": name_en, + "prop": "langlinks", + "lllimit": 500, + "format": "json", + } r = requests.get(url, params=params) if not r: - raise ValueError('Could not find wikipedia page: {0}'.format(name_en)) + raise ValueError("Could not find wikipedia page: {0}".format(name_en)) out = r.json() names = {} - pages = out['query']['pages'] + pages = out["query"]["pages"] for page in pages: - for langlink in pages[page].get('langlinks', []): - names[langlink['lang']] = langlink['*'] + for langlink in pages[page].get("langlinks", []): + names[langlink["lang"]] = langlink["*"] return names @@ -90,17 +210,18 @@ def __iter__(self): length = len(self.NAMES_EN) for z, name_en in enumerate(self.NAMES_EN, 1): element = Element(z) - language = Language('en') + language = Language("en") prop = ElementName(WIKIPEDIA, element, language, name_en) - logger.debug('Parsed: {0}'.format(prop)) + logger.debug("Parsed: {0}".format(prop)) yield prop names = self._find_wikipedia_names(name_en) for code, name in names.items(): - if code not in self.LANGUAGES: continue + if code not in self.LANGUAGES: + continue language = Language(code) prop = ElementName(WIKIPEDIA, element, language, name) - logger.debug('Parsed: {0}'.format(prop)) + logger.debug("Parsed: {0}".format(prop)) yield prop self.update(int((z - 1) / length * 100.0)) diff --git a/pyxray/property.py b/pyxray/property.py index c3788af..6c74a71 100644 --- a/pyxray/property.py +++ b/pyxray/property.py @@ -8,10 +8,19 @@ # Third party modules. # Local modules. -from pyxray.descriptor import Element, Reference, AtomicShell, AtomicSubshell, XrayTransition, Notation, Language +from pyxray.descriptor import ( + Element, + Reference, + AtomicShell, + AtomicSubshell, + XrayTransition, + Notation, + Language, +) # Globals and constants variables. + @dataclasses.dataclass(frozen=True) class ElementSymbol: reference: Reference @@ -20,11 +29,12 @@ class ElementSymbol: def __post_init__(self): if len(self.value) == 0 or len(self.value) > 3: - raise ValueError('Symbol should be between 1 and 3 characters') + raise ValueError("Symbol should be between 1 and 3 characters") if not self.value[0].isupper(): raise ValueError("Symbol should start with a capital letter") + @dataclasses.dataclass(frozen=True) class ElementName: reference: Reference @@ -34,7 +44,8 @@ class ElementName: def __post_init__(self): if not self.value: - raise ValueError('A name must be specified') + raise ValueError("A name must be specified") + @dataclasses.dataclass(frozen=True) class ElementAtomicWeight: @@ -44,7 +55,8 @@ class ElementAtomicWeight: def __post_init__(self): if self.value <= 0.0: - raise ValueError('Value must be greater than 0.0') + raise ValueError("Value must be greater than 0.0") + @dataclasses.dataclass(frozen=True) class ElementMassDensity: @@ -54,7 +66,8 @@ class ElementMassDensity: def __post_init__(self): if self.value_kg_per_m3 <= 0.0: - raise ValueError('Value must be greater than 0.0') + raise ValueError("Value must be greater than 0.0") + @dataclasses.dataclass(frozen=True) class AtomicShellNotation: @@ -66,6 +79,7 @@ class AtomicShellNotation: html: str latex: str + @dataclasses.dataclass(frozen=True) class AtomicSubshellNotation: reference: Reference @@ -76,6 +90,7 @@ class AtomicSubshellNotation: html: str latex: str + @dataclasses.dataclass(frozen=True) class AtomicSubshellBindingEnergy: reference: Reference @@ -83,6 +98,7 @@ class AtomicSubshellBindingEnergy: atomic_subshell: AtomicSubshell value_eV: float + @dataclasses.dataclass(frozen=True) class AtomicSubshellRadiativeWidth: reference: Reference @@ -90,6 +106,7 @@ class AtomicSubshellRadiativeWidth: atomic_subshell: AtomicSubshell value_eV: float + @dataclasses.dataclass(frozen=True) class AtomicSubshellNonRadiativeWidth: reference: Reference @@ -97,6 +114,7 @@ class AtomicSubshellNonRadiativeWidth: atomic_subshell: AtomicSubshell value_eV: float + @dataclasses.dataclass(frozen=True) class AtomicSubshellOccupancy: reference: Reference @@ -104,6 +122,7 @@ class AtomicSubshellOccupancy: atomic_subshell: AtomicSubshell value: float + @dataclasses.dataclass(frozen=True) class XrayTransitionNotation: reference: Reference @@ -114,6 +133,7 @@ class XrayTransitionNotation: html: str latex: str + @dataclasses.dataclass(frozen=True) class XrayTransitionEnergy: reference: Reference @@ -121,6 +141,7 @@ class XrayTransitionEnergy: xray_transition: XrayTransition value_eV: float + @dataclasses.dataclass(frozen=True) class XrayTransitionProbability: reference: Reference @@ -128,6 +149,7 @@ class XrayTransitionProbability: xray_transition: XrayTransition value: float + @dataclasses.dataclass(frozen=True) class XrayTransitionRelativeWeight: reference: Reference diff --git a/pyxray/sql/base.py b/pyxray/sql/base.py index be43d4b..c0de105 100644 --- a/pyxray/sql/base.py +++ b/pyxray/sql/base.py @@ -16,14 +16,18 @@ # Globals and constants variables. logger = logging.getLogger(__name__) + def camelcase_to_words(text): - return re.sub('([a-z0-9])([A-Z])', r'\1 \2', text) + return re.sub("([a-z0-9])([A-Z])", r"\1 \2", text) + class SqlBase: - FIELDS_TO_SQLTYPE = {int: sqlalchemy.Integer, - float: sqlalchemy.Float, - str: sqlalchemy.String} + FIELDS_TO_SQLTYPE = { + int: sqlalchemy.Integer, + float: sqlalchemy.Float, + str: sqlalchemy.String, + } def __init__(self, engine): self.engine = engine @@ -41,7 +45,7 @@ def _get_table_name(self, dataclass): """ if not inspect.isclass(dataclass): dataclass = type(dataclass) - return '_'.join(camelcase_to_words(dataclass.__name__).split()).lower() + return "_".join(camelcase_to_words(dataclass.__name__).split()).lower() def _create_table(self, table_name, dataclass): """ @@ -54,25 +58,31 @@ def _create_table(self, table_name, dataclass): Returns: :class:`sqlalchemy.Table`: table instance """ - columns = [sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True)] + columns = [sqlalchemy.Column("id", sqlalchemy.Integer, primary_key=True)] for field in dataclasses.fields(dataclass): if dataclasses.is_dataclass(field.type): subtable = self.require_table(field.type) - column = sqlalchemy.Column(field.name + '_id', None, sqlalchemy.ForeignKey(subtable.name + '.id')) + column = sqlalchemy.Column( + field.name + "_id", + None, + sqlalchemy.ForeignKey(subtable.name + ".id"), + ) elif field.type in self.FIELDS_TO_SQLTYPE: nullable = field.default is None - if field.type == str and (field.name.startswith('key') or field.name.endswith('key')): - columntype = sqlalchemy.String(collation='NOCASE') + if field.type == str and ( + field.name.startswith("key") or field.name.endswith("key") + ): + columntype = sqlalchemy.String(collation="NOCASE") else: columntype = self.FIELDS_TO_SQLTYPE[field.type] column = sqlalchemy.Column(field.name, columntype, nullable=nullable) else: - raise ValueError('Unknown field: {}'.format(field.type)) + raise ValueError("Unknown field: {}".format(field.type)) columns.append(column) @@ -122,14 +132,16 @@ def _get_row(self, dataclass): if dataclasses.is_dataclass(field.type): row_id = self._get_row(value) - clause = table.c[name + '_id'] == row_id + clause = table.c[name + "_id"] == row_id else: clause = table.c[name] == value clauses.append(clause) - statement = sqlalchemy.sql.select([table.c.id]).where(sqlalchemy.sql.and_(*clauses)) + statement = sqlalchemy.sql.select([table.c.id]).where( + sqlalchemy.sql.and_(*clauses) + ) with self.engine.begin() as conn: return conn.execute(statement).scalar() diff --git a/pyxray/sql/build.py b/pyxray/sql/build.py index e76cb43..99042a7 100644 --- a/pyxray/sql/build.py +++ b/pyxray/sql/build.py @@ -16,8 +16,8 @@ # Globals and constants variables. logger = logging.getLogger(__name__) -class SqlDatabaseBuilder(SqlBase): +class SqlDatabaseBuilder(SqlBase): def _convert_dataclass_to_params(self, dataclass): params = {} for field in dataclasses.fields(dataclass): @@ -26,7 +26,7 @@ def _convert_dataclass_to_params(self, dataclass): if dataclasses.is_dataclass(value): row_id = self.insert(value, check_duplicate=True) - params[name + '_id'] = row_id + params[name + "_id"] = row_id else: params[name] = value @@ -68,7 +68,7 @@ def insert_many(self, list_dataclass): list_params = [] for dataclass in list_dataclass: if type(dataclass) != clasz: - raise ValueError('All dataclasses do not have the same type') + raise ValueError("All dataclasses do not have the same type") params = self._convert_dataclass_to_params(dataclass) list_params.append(params) @@ -87,15 +87,15 @@ def build(self): Find all parsers and insert their properties in the database. """ parsers = self._find_parsers() - logger.info('Found {:d} parsers'.format(len(parsers))) + logger.info("Found {:d} parsers".format(len(parsers))) - for name, parser in tqdm.tqdm(parsers, desc='Building database'): + for name, parser in tqdm.tqdm(parsers, desc="Building database"): buffer = {} - for prop in tqdm.tqdm(parser, desc='Processing {}'.format(name)): + for prop in tqdm.tqdm(parser, desc="Processing {}".format(name)): buffer.setdefault(type(prop), []).append(prop) - for list_dataclass in tqdm.tqdm(buffer.values(), desc='Inserting {}'.format(name)): + for list_dataclass in tqdm.tqdm( + buffer.values(), desc="Inserting {}".format(name) + ): self.insert_many(list_dataclass) - - diff --git a/pyxray/sql/data.py b/pyxray/sql/data.py index 8f7e8a4..7c58462 100644 --- a/pyxray/sql/data.py +++ b/pyxray/sql/data.py @@ -16,8 +16,8 @@ # Globals and constants variables. logger = logging.getLogger(__name__) -class StatementBuilder: +class StatementBuilder: def __init__(self): self._columns = [] self._joins = {} @@ -56,28 +56,29 @@ def build(self): return statement.where(sqlalchemy.sql.and_(*self._clauses)) -class SqlDatabase(_DatabaseMixin, SqlBase): +class SqlDatabase(_DatabaseMixin, SqlBase): def __init__(self, engine): super().__init__(engine) self._preferred_references = [] def _expand_atomic_subshell(self, atomic_subshell): - if hasattr(atomic_subshell, 'principal_quantum_number') and \ - hasattr(atomic_subshell, 'azimuthal_quantum_number') and \ - hasattr(atomic_subshell, 'total_angular_momentum_nominator'): + if ( + hasattr(atomic_subshell, "principal_quantum_number") + and hasattr(atomic_subshell, "azimuthal_quantum_number") + and hasattr(atomic_subshell, "total_angular_momentum_nominator") + ): n = atomic_subshell.atomic_shell.principal_quantum_number l = atomic_subshell.azimuthal_quantum_number j_n = atomic_subshell.total_angular_momentum_nominator - elif isinstance(atomic_subshell, Sequence) and \ - len(atomic_subshell) == 3: + elif isinstance(atomic_subshell, Sequence) and len(atomic_subshell) == 3: n = atomic_subshell[0] l = atomic_subshell[1] j_n = atomic_subshell[2] else: - raise NotFound('Cannot parse atomic subshell: {}'.format(atomic_subshell)) + raise NotFound("Cannot parse atomic subshell: {}".format(atomic_subshell)) return n, l, j_n @@ -90,132 +91,222 @@ def _expand_xray_transition(self, xray_transition): dst_l = xray_transition.destination_azimuthal_quantum_number dst_j_n = xray_transition.destination_total_angular_momentum_nominator - elif isinstance(xray_transition, Sequence) and \ - len(xray_transition) >= 2: + elif isinstance(xray_transition, Sequence) and len(xray_transition) >= 2: src_n, src_l, src_j_n = self._expand_atomic_subshell(xray_transition[0]) dst_n, dst_l, dst_j_n = self._expand_atomic_subshell(xray_transition[1]) else: - raise NotFound('Cannot parse X-ray transition: {}'.format(xray_transition)) + raise NotFound("Cannot parse X-ray transition: {}".format(xray_transition)) return src_n, src_l, src_j_n, dst_n, dst_l, dst_j_n - def _update_element(self, builder, table, element, column='element_id'): - if hasattr(element, 'atomic_number'): + def _update_element(self, builder, table, element, column="element_id"): + if hasattr(element, "atomic_number"): element = element.atomic_number if isinstance(element, str): table_name = self.require_table(property.ElementName) - builder.add_join(table, table_name, table.c[column] == table_name.c['element_id']) - clause_name = table_name.c['value'] == element + builder.add_join( + table, table_name, table.c[column] == table_name.c["element_id"] + ) + clause_name = table_name.c["value"] == element table_symbol = self.require_table(property.ElementSymbol) - builder.add_join(table, table_symbol, table.c[column] == table_symbol.c['element_id']) - clause_symbol = table_symbol.c['value'] == element + builder.add_join( + table, table_symbol, table.c[column] == table_symbol.c["element_id"] + ) + clause_symbol = table_symbol.c["value"] == element builder.add_clause(sqlalchemy.sql.or_(clause_name, clause_symbol)) elif isinstance(element, int): table_element = self.require_table(descriptor.Element) - builder.add_join(table, table_element, table.c[column] == table_element.c['id']) - builder.add_clause(table_element.c['atomic_number'] == element) + builder.add_join( + table, table_element, table.c[column] == table_element.c["id"] + ) + builder.add_clause(table_element.c["atomic_number"] == element) else: - raise NotFound('Cannot parse element: {}'.format(element)) + raise NotFound("Cannot parse element: {}".format(element)) - def _update_atomic_shell(self, builder, table, atomic_shell, column='atomic_shell_id'): - if hasattr(atomic_shell, 'principal_quantum_number'): + def _update_atomic_shell( + self, builder, table, atomic_shell, column="atomic_shell_id" + ): + if hasattr(atomic_shell, "principal_quantum_number"): atomic_shell = atomic_shell.principal_quantum_number if isinstance(atomic_shell, str): table_notation = self.require_table(property.AtomicShellNotation) - builder.add_join(table, table_notation, table.c[column] == table_notation.c['atomic_shell_id']) - builder.add_clause(sqlalchemy.sql.or_(table_notation.c['ascii'] == atomic_shell, - table_notation.c['utf16'] == atomic_shell)) + builder.add_join( + table, + table_notation, + table.c[column] == table_notation.c["atomic_shell_id"], + ) + builder.add_clause( + sqlalchemy.sql.or_( + table_notation.c["ascii"] == atomic_shell, + table_notation.c["utf16"] == atomic_shell, + ) + ) elif isinstance(atomic_shell, int): table_atomic_shell = self.require_table(descriptor.AtomicShell) - builder.add_join(table, table_atomic_shell, table.c[column] == table_atomic_shell.c['id']) - builder.add_clause(table_atomic_shell.c['principal_quantum_number'] == atomic_shell) + builder.add_join( + table, table_atomic_shell, table.c[column] == table_atomic_shell.c["id"] + ) + builder.add_clause( + table_atomic_shell.c["principal_quantum_number"] == atomic_shell + ) else: - raise NotFound('Cannot parse atomic shell: {}'.format(atomic_shell)) + raise NotFound("Cannot parse atomic shell: {}".format(atomic_shell)) - def _update_atomic_subshell(self, builder, table, atomic_subshell, column='atomic_subshell_id'): + def _update_atomic_subshell( + self, builder, table, atomic_subshell, column="atomic_subshell_id" + ): if isinstance(atomic_subshell, str): table_notation = self.require_table(property.AtomicSubshellNotation) - builder.add_join(table, table_notation, table.c[column] == table_notation.c['atomic_subshell_id']) - builder.add_clause(sqlalchemy.sql.or_(table_notation.c['ascii'] == atomic_subshell, - table_notation.c['utf16'] == atomic_subshell)) + builder.add_join( + table, + table_notation, + table.c[column] == table_notation.c["atomic_subshell_id"], + ) + builder.add_clause( + sqlalchemy.sql.or_( + table_notation.c["ascii"] == atomic_subshell, + table_notation.c["utf16"] == atomic_subshell, + ) + ) else: n, l, j_n = self._expand_atomic_subshell(atomic_subshell) table_atomic_subshell = self.require_table(descriptor.AtomicSubshell) - builder.add_join(table, table_atomic_subshell, table.c[column] == table_atomic_subshell.c['id']) - builder.add_clause(table_atomic_subshell.c['principal_quantum_number'] == n) - builder.add_clause(table_atomic_subshell.c['azimuthal_quantum_number'] == l) - builder.add_clause(table_atomic_subshell.c['total_angular_momentum_nominator'] == j_n) - - def _update_xray_transition(self, builder, table, xray_transition, column='xray_transition_id', search=False): + builder.add_join( + table, + table_atomic_subshell, + table.c[column] == table_atomic_subshell.c["id"], + ) + builder.add_clause(table_atomic_subshell.c["principal_quantum_number"] == n) + builder.add_clause(table_atomic_subshell.c["azimuthal_quantum_number"] == l) + builder.add_clause( + table_atomic_subshell.c["total_angular_momentum_nominator"] == j_n + ) + + def _update_xray_transition( + self, builder, table, xray_transition, column="xray_transition_id", search=False + ): if isinstance(xray_transition, str): table_notation = self.require_table(property.XrayTransitionNotation) - builder.add_join(table, table_notation, table.c[column] == table_notation.c['xray_transition_id']) - builder.add_clause(sqlalchemy.sql.or_(table_notation.c['ascii'] == xray_transition, - table_notation.c['utf16'] == xray_transition)) + builder.add_join( + table, + table_notation, + table.c[column] == table_notation.c["xray_transition_id"], + ) + builder.add_clause( + sqlalchemy.sql.or_( + table_notation.c["ascii"] == xray_transition, + table_notation.c["utf16"] == xray_transition, + ) + ) else: - src_n, src_l, src_j_n, dst_n, dst_l, dst_j_n = self._expand_xray_transition(xray_transition) + src_n, src_l, src_j_n, dst_n, dst_l, dst_j_n = self._expand_xray_transition( + xray_transition + ) table_xray_transition = self.require_table(descriptor.XrayTransition) - builder.add_join(table, table_xray_transition, table.c[column] == table_xray_transition.c['id']) + builder.add_join( + table, + table_xray_transition, + table.c[column] == table_xray_transition.c["id"], + ) if search: + def create_clause(column, value): if value is not None: return table_xray_transition.c[column] == value else: return table_xray_transition.c[column] != None - builder.add_clause(create_clause('source_principal_quantum_number', src_n)) - builder.add_clause(create_clause('source_azimuthal_quantum_number', src_l)) - builder.add_clause(create_clause('source_total_angular_momentum_nominator', src_j_n)) - builder.add_clause(create_clause('destination_principal_quantum_number', dst_n)) - builder.add_clause(create_clause('destination_azimuthal_quantum_number', dst_l)) - builder.add_clause(create_clause('destination_total_angular_momentum_nominator', dst_j_n)) + builder.add_clause( + create_clause("source_principal_quantum_number", src_n) + ) + builder.add_clause( + create_clause("source_azimuthal_quantum_number", src_l) + ) + builder.add_clause( + create_clause("source_total_angular_momentum_nominator", src_j_n) + ) + builder.add_clause( + create_clause("destination_principal_quantum_number", dst_n) + ) + builder.add_clause( + create_clause("destination_azimuthal_quantum_number", dst_l) + ) + builder.add_clause( + create_clause( + "destination_total_angular_momentum_nominator", dst_j_n + ) + ) else: - builder.add_clause(table_xray_transition.c['source_principal_quantum_number'] == src_n) - builder.add_clause(table_xray_transition.c['source_azimuthal_quantum_number'] == src_l) - builder.add_clause(table_xray_transition.c['source_total_angular_momentum_nominator'] == src_j_n) - builder.add_clause(table_xray_transition.c['destination_principal_quantum_number'] == dst_n) - builder.add_clause(table_xray_transition.c['destination_azimuthal_quantum_number'] == dst_l) - builder.add_clause(table_xray_transition.c['destination_total_angular_momentum_nominator'] == dst_j_n) - - def _update_reference(self, builder, table, reference, column='reference_id'): + builder.add_clause( + table_xray_transition.c["source_principal_quantum_number"] == src_n + ) + builder.add_clause( + table_xray_transition.c["source_azimuthal_quantum_number"] == src_l + ) + builder.add_clause( + table_xray_transition.c["source_total_angular_momentum_nominator"] + == src_j_n + ) + builder.add_clause( + table_xray_transition.c["destination_principal_quantum_number"] + == dst_n + ) + builder.add_clause( + table_xray_transition.c["destination_azimuthal_quantum_number"] + == dst_l + ) + builder.add_clause( + table_xray_transition.c[ + "destination_total_angular_momentum_nominator" + ] + == dst_j_n + ) + + def _update_reference(self, builder, table, reference, column="reference_id"): if isinstance(reference, descriptor.Reference): reference = reference.bibtexkey table_reference = self.require_table(descriptor.Reference) - builder.add_column(table_reference.c['bibtexkey']) - builder.add_join(table, table_reference, table.c[column] == table_reference.c['id']) + builder.add_column(table_reference.c["bibtexkey"]) + builder.add_join( + table, table_reference, table.c[column] == table_reference.c["id"] + ) if reference: - builder.add_clause(table_reference.c['bibtexkey'] == reference) + builder.add_clause(table_reference.c["bibtexkey"] == reference) def _update_language(self, builder, table, language): if isinstance(language, descriptor.Language): language = language.key table_language = self.require_table(descriptor.Language) - builder.add_join(table, table_language, table.c['language_id'] == table_language.c['id']) - builder.add_clause(table_language.c['key'] == language) + builder.add_join( + table, table_language, table.c["language_id"] == table_language.c["id"] + ) + builder.add_clause(table_language.c["key"] == language) def _update_notation(self, builder, table, notation): if isinstance(notation, descriptor.Notation): notation = notation.key table_notation = self.require_table(descriptor.Notation) - builder.add_join(table, table_notation, table.c['notation_id'] == table_notation.c['id']) - builder.add_clause(table_notation.c['key'] == notation) + builder.add_join( + table, table_notation, table.c["notation_id"] == table_notation.c["id"] + ) + builder.add_clause(table_notation.c["key"] == notation) def _execute(self, builder, remove_bibtexkey_column=True): statement = builder.build() @@ -232,8 +323,8 @@ def _execute(self, builder, remove_bibtexkey_column=True): row = rows[0] else: - dictrows = dict((dict(row).get('bibtexkey', None), row) for row in rows) - row = rows[0] # fall back + dictrows = dict((dict(row).get("bibtexkey", None), row) for row in rows) + row = rows[0] # fall back for bibtexkey in self._preferred_references: if bibtexkey in dictrows: row = dictrows[bibtexkey] @@ -241,7 +332,7 @@ def _execute(self, builder, remove_bibtexkey_column=True): row = dict(row) if remove_bibtexkey_column: - row.pop('bibtexkey', None) + row.pop("bibtexkey", None) if len(row) == 1: return row.popitem()[1] @@ -262,7 +353,7 @@ def _execute_many(self, builder, remove_bibtexkey_column=True): for row in rows: row = dict(row) if remove_bibtexkey_column: - row.pop('bibtexkey', None) + row.pop("bibtexkey", None) outrows.append(list(row.values())) return outrows @@ -279,7 +370,7 @@ def add_preferred_reference(self, reference): table = self.require_table(descriptor.Reference) builder = StatementBuilder() - self._update_reference(builder, table, reference, 'id') + self._update_reference(builder, table, reference, "id") bibtexkey = self._execute(builder, remove_bibtexkey_column=False) @@ -292,8 +383,8 @@ def element(self, element): table = self.require_table(descriptor.Element) builder = StatementBuilder() - builder.add_column(table.c['atomic_number']) - self._update_element(builder, table, element, 'id') + builder.add_column(table.c["atomic_number"]) + self._update_element(builder, table, element, "id") atomic_number = self._execute(builder) return descriptor.Element(atomic_number) @@ -302,8 +393,8 @@ def element_atomic_number(self, element): table = self.require_table(descriptor.Element) builder = StatementBuilder() - builder.add_column(table.c['atomic_number']) - self._update_element(builder, table, element, 'id') + builder.add_column(table.c["atomic_number"]) + self._update_element(builder, table, element, "id") return self._execute(builder) @@ -311,17 +402,17 @@ def element_symbol(self, element, reference=None): table = self.require_table(property.ElementSymbol) builder = StatementBuilder() - builder.add_column(table.c['value']) + builder.add_column(table.c["value"]) self._update_element(builder, table, element) self._update_reference(builder, table, reference) return self._execute(builder) - def element_name(self, element, language='en', reference=None): + def element_name(self, element, language="en", reference=None): table = self.require_table(property.ElementName) builder = StatementBuilder() - builder.add_column(table.c['value']) + builder.add_column(table.c["value"]) self._update_element(builder, table, element) self._update_language(builder, table, language) self._update_reference(builder, table, reference) @@ -332,7 +423,7 @@ def element_atomic_weight(self, element, reference=None): table = self.require_table(property.ElementAtomicWeight) builder = StatementBuilder() - builder.add_column(table.c['value']) + builder.add_column(table.c["value"]) self._update_element(builder, table, element) self._update_reference(builder, table, reference) @@ -342,7 +433,7 @@ def element_mass_density_kg_per_m3(self, element, reference=None): table = self.require_table(property.ElementMassDensity) builder = StatementBuilder() - builder.add_column(table.c['value_kg_per_m3']) + builder.add_column(table.c["value_kg_per_m3"]) self._update_element(builder, table, element) self._update_reference(builder, table, reference) @@ -353,47 +444,71 @@ def element_xray_transitions(self, element, xray_transition=None, reference=None table_probability = self.require_table(property.XrayTransitionProbability) builder = StatementBuilder() - builder.add_column(table_xray.c['source_principal_quantum_number']) - builder.add_column(table_xray.c['source_azimuthal_quantum_number']) - builder.add_column(table_xray.c['source_total_angular_momentum_nominator']) - builder.add_column(table_xray.c['destination_principal_quantum_number']) - builder.add_column(table_xray.c['destination_azimuthal_quantum_number']) - builder.add_column(table_xray.c['destination_total_angular_momentum_nominator']) - builder.add_join(table_probability, table_xray, table_probability.c['xray_transition_id'] == table_xray.c['id']) - builder.add_clause(table_probability.c['value'] > 0.0) + builder.add_column(table_xray.c["source_principal_quantum_number"]) + builder.add_column(table_xray.c["source_azimuthal_quantum_number"]) + builder.add_column(table_xray.c["source_total_angular_momentum_nominator"]) + builder.add_column(table_xray.c["destination_principal_quantum_number"]) + builder.add_column(table_xray.c["destination_azimuthal_quantum_number"]) + builder.add_column(table_xray.c["destination_total_angular_momentum_nominator"]) + builder.add_join( + table_probability, + table_xray, + table_probability.c["xray_transition_id"] == table_xray.c["id"], + ) + builder.add_clause(table_probability.c["value"] > 0.0) self._update_element(builder, table_probability, element) self._update_reference(builder, table_probability, reference) if xray_transition is not None: - self._update_xray_transition(builder, table_probability, xray_transition, search=True) + self._update_xray_transition( + builder, table_probability, xray_transition, search=True + ) transitions = [] try: - for src_n, src_l, src_j_n, dst_n, dst_l, dst_j_n in self._execute_many(builder): - transition = descriptor.XrayTransition(src_n, src_l, src_j_n, dst_n, dst_l, dst_j_n) + for src_n, src_l, src_j_n, dst_n, dst_l, dst_j_n in self._execute_many( + builder + ): + transition = descriptor.XrayTransition( + src_n, src_l, src_j_n, dst_n, dst_l, dst_j_n + ) transitions.append(transition) except NotFound: logger.info("No transition found for {}".format(element)) if len(transitions) == 0: - table_relative_weight = self.require_table(property.XrayTransitionRelativeWeight) + table_relative_weight = self.require_table( + property.XrayTransitionRelativeWeight + ) builder = StatementBuilder() - builder.add_column(table_xray.c['source_principal_quantum_number']) - builder.add_column(table_xray.c['source_azimuthal_quantum_number']) - builder.add_column(table_xray.c['source_total_angular_momentum_nominator']) - builder.add_column(table_xray.c['destination_principal_quantum_number']) - builder.add_column(table_xray.c['destination_azimuthal_quantum_number']) - builder.add_column(table_xray.c['destination_total_angular_momentum_nominator']) - builder.add_join(table_relative_weight, table_xray, table_relative_weight.c['xray_transition_id'] == table_xray.c['id']) - builder.add_clause(table_relative_weight.c['value'] > 0.0) + builder.add_column(table_xray.c["source_principal_quantum_number"]) + builder.add_column(table_xray.c["source_azimuthal_quantum_number"]) + builder.add_column(table_xray.c["source_total_angular_momentum_nominator"]) + builder.add_column(table_xray.c["destination_principal_quantum_number"]) + builder.add_column(table_xray.c["destination_azimuthal_quantum_number"]) + builder.add_column( + table_xray.c["destination_total_angular_momentum_nominator"] + ) + builder.add_join( + table_relative_weight, + table_xray, + table_relative_weight.c["xray_transition_id"] == table_xray.c["id"], + ) + builder.add_clause(table_relative_weight.c["value"] > 0.0) self._update_element(builder, table_relative_weight, element) self._update_reference(builder, table_relative_weight, reference) if xray_transition is not None: - self._update_xray_transition(builder, table_relative_weight, xray_transition, search=True) + self._update_xray_transition( + builder, table_relative_weight, xray_transition, search=True + ) transitions = [] try: - for src_n, src_l, src_j_n, dst_n, dst_l, dst_j_n in self._execute_many(builder): - transition = descriptor.XrayTransition(src_n, src_l, src_j_n, dst_n, dst_l, dst_j_n) + for src_n, src_l, src_j_n, dst_n, dst_l, dst_j_n in self._execute_many( + builder + ): + transition = descriptor.XrayTransition( + src_n, src_l, src_j_n, dst_n, dst_l, dst_j_n + ) transitions.append(transition) except NotFound: logger.info("No transition found for {}".format(element)) @@ -408,15 +523,19 @@ def element_xray_transition(self, element, xray_transition, reference=None): table_probability = self.require_table(property.XrayTransitionProbability) builder = StatementBuilder() - builder.add_column(table_xray.c['source_principal_quantum_number']) - builder.add_column(table_xray.c['source_azimuthal_quantum_number']) - builder.add_column(table_xray.c['source_total_angular_momentum_nominator']) - builder.add_column(table_xray.c['destination_principal_quantum_number']) - builder.add_column(table_xray.c['destination_azimuthal_quantum_number']) - builder.add_column(table_xray.c['destination_total_angular_momentum_nominator']) - builder.add_join(table_probability, table_xray, table_probability.c['xray_transition_id'] == table_xray.c['id']) - builder.add_clause(table_probability.c['value'] > 0.0) - self._update_xray_transition(builder, table_xray, xray_transition, 'id') + builder.add_column(table_xray.c["source_principal_quantum_number"]) + builder.add_column(table_xray.c["source_azimuthal_quantum_number"]) + builder.add_column(table_xray.c["source_total_angular_momentum_nominator"]) + builder.add_column(table_xray.c["destination_principal_quantum_number"]) + builder.add_column(table_xray.c["destination_azimuthal_quantum_number"]) + builder.add_column(table_xray.c["destination_total_angular_momentum_nominator"]) + builder.add_join( + table_probability, + table_xray, + table_probability.c["xray_transition_id"] == table_xray.c["id"], + ) + builder.add_clause(table_probability.c["value"] > 0.0) + self._update_xray_transition(builder, table_xray, xray_transition, "id") self._update_element(builder, table_probability, element) self._update_reference(builder, table_probability, reference) @@ -427,18 +546,20 @@ def atomic_shell(self, atomic_shell): table = self.require_table(descriptor.AtomicShell) builder = StatementBuilder() - builder.add_column(table.c['principal_quantum_number']) - self._update_atomic_shell(builder, table, atomic_shell, 'id') + builder.add_column(table.c["principal_quantum_number"]) + self._update_atomic_shell(builder, table, atomic_shell, "id") principal_quantum_number = self._execute(builder) return descriptor.AtomicShell(principal_quantum_number) - def atomic_shell_notation(self, atomic_shell, notation, encoding='utf16', reference=None): + def atomic_shell_notation( + self, atomic_shell, notation, encoding="utf16", reference=None + ): table = self.require_table(property.AtomicShellNotation) builder = StatementBuilder() builder.add_column(table.c[encoding]) - self._update_atomic_shell(builder, table, atomic_shell, 'id') + self._update_atomic_shell(builder, table, atomic_shell, "id") self._update_notation(builder, table, notation) self._update_reference(builder, table, reference) @@ -448,15 +569,17 @@ def atomic_subshell(self, atomic_subshell): table = self.require_table(descriptor.AtomicSubshell) builder = StatementBuilder() - builder.add_column(table.c['principal_quantum_number']) - builder.add_column(table.c['azimuthal_quantum_number']) - builder.add_column(table.c['total_angular_momentum_nominator']) - self._update_atomic_subshell(builder, table, atomic_subshell, 'id') + builder.add_column(table.c["principal_quantum_number"]) + builder.add_column(table.c["azimuthal_quantum_number"]) + builder.add_column(table.c["total_angular_momentum_nominator"]) + self._update_atomic_subshell(builder, table, atomic_subshell, "id") n, l, j_n = self._execute(builder) return descriptor.AtomicSubshell(n, l, j_n) - def atomic_subshell_notation(self, atomic_subshell, notation, encoding='utf16', reference=None): + def atomic_subshell_notation( + self, atomic_subshell, notation, encoding="utf16", reference=None + ): table = self.require_table(property.AtomicSubshellNotation) builder = StatementBuilder() @@ -467,33 +590,39 @@ def atomic_subshell_notation(self, atomic_subshell, notation, encoding='utf16', return self._execute(builder) - def atomic_subshell_binding_energy_eV(self, element, atomic_subshell, reference=None): + def atomic_subshell_binding_energy_eV( + self, element, atomic_subshell, reference=None + ): table = self.require_table(property.AtomicSubshellBindingEnergy) builder = StatementBuilder() - builder.add_column(table.c['value_eV']) + builder.add_column(table.c["value_eV"]) self._update_element(builder, table, element) self._update_atomic_subshell(builder, table, atomic_subshell) self._update_reference(builder, table, reference) return self._execute(builder) - def atomic_subshell_radiative_width_eV(self, element, atomic_subshell, reference=None): + def atomic_subshell_radiative_width_eV( + self, element, atomic_subshell, reference=None + ): table = self.require_table(property.AtomicSubshellRadiativeWidth) builder = StatementBuilder() - builder.add_column(table.c['value_eV']) + builder.add_column(table.c["value_eV"]) self._update_element(builder, table, element) self._update_atomic_subshell(builder, table, atomic_subshell) self._update_reference(builder, table, reference) return self._execute(builder) - def atomic_subshell_nonradiative_width_eV(self, element, atomic_subshell, reference=None): + def atomic_subshell_nonradiative_width_eV( + self, element, atomic_subshell, reference=None + ): table = self.require_table(property.AtomicSubshellNonRadiativeWidth) builder = StatementBuilder() - builder.add_column(table.c['value_eV']) + builder.add_column(table.c["value_eV"]) self._update_element(builder, table, element) self._update_atomic_subshell(builder, table, atomic_subshell) self._update_reference(builder, table, reference) @@ -504,7 +633,7 @@ def atomic_subshell_occupancy(self, element, atomic_subshell, reference=None): table = self.require_table(property.AtomicSubshellOccupancy) builder = StatementBuilder() - builder.add_column(table.c['value']) + builder.add_column(table.c["value"]) self._update_element(builder, table, element) self._update_atomic_subshell(builder, table, atomic_subshell) self._update_reference(builder, table, reference) @@ -515,18 +644,20 @@ def xray_transition(self, xray_transition): table = self.require_table(descriptor.XrayTransition) builder = StatementBuilder() - builder.add_column(table.c['source_principal_quantum_number']) - builder.add_column(table.c['source_azimuthal_quantum_number']) - builder.add_column(table.c['source_total_angular_momentum_nominator']) - builder.add_column(table.c['destination_principal_quantum_number']) - builder.add_column(table.c['destination_azimuthal_quantum_number']) - builder.add_column(table.c['destination_total_angular_momentum_nominator']) - self._update_xray_transition(builder, table, xray_transition, 'id') + builder.add_column(table.c["source_principal_quantum_number"]) + builder.add_column(table.c["source_azimuthal_quantum_number"]) + builder.add_column(table.c["source_total_angular_momentum_nominator"]) + builder.add_column(table.c["destination_principal_quantum_number"]) + builder.add_column(table.c["destination_azimuthal_quantum_number"]) + builder.add_column(table.c["destination_total_angular_momentum_nominator"]) + self._update_xray_transition(builder, table, xray_transition, "id") src_n, src_l, src_j_n, dst_n, dst_l, dst_j_n = self._execute(builder) return descriptor.XrayTransition(src_n, src_l, src_j_n, dst_n, dst_l, dst_j_n) - def xray_transition_notation(self, xray_transition, notation, encoding='utf16', reference=None): + def xray_transition_notation( + self, xray_transition, notation, encoding="utf16", reference=None + ): table = self.require_table(property.XrayTransitionNotation) builder = StatementBuilder() @@ -541,7 +672,7 @@ def xray_transition_energy_eV(self, element, xray_transition, reference=None): table = self.require_table(property.XrayTransitionEnergy) builder = StatementBuilder() - builder.add_column(table.c['value_eV']) + builder.add_column(table.c["value_eV"]) self._update_element(builder, table, element) self._update_xray_transition(builder, table, xray_transition) self._update_reference(builder, table, reference) @@ -552,7 +683,7 @@ def xray_transition_probability(self, element, xray_transition, reference=None): table = self.require_table(property.XrayTransitionProbability) builder = StatementBuilder() - builder.add_column(table.c['value']) + builder.add_column(table.c["value"]) self._update_element(builder, table, element) self._update_xray_transition(builder, table, xray_transition) self._update_reference(builder, table, reference) @@ -563,7 +694,7 @@ def xray_transition_relative_weight(self, element, xray_transition, reference=No table = self.require_table(property.XrayTransitionRelativeWeight) builder = StatementBuilder() - builder.add_column(table.c['value']) + builder.add_column(table.c["value"]) self._update_element(builder, table, element) self._update_xray_transition(builder, table, xray_transition) self._update_reference(builder, table, reference) diff --git a/pyxray/util.py b/pyxray/util.py index cecef49..f26bd62 100644 --- a/pyxray/util.py +++ b/pyxray/util.py @@ -13,8 +13,10 @@ c = 299792458 h_eVs = 4.13566733e-15 + def energy_to_wavelength_m(energy_eV): return h_eVs * c / energy_eV + def wavelength_to_energy_eV(wavelength_m): - return h_eVs * c / wavelength_m \ No newline at end of file + return h_eVs * c / wavelength_m diff --git a/requirements.txt b/requirements.txt index f3bbcb5..818c462 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -tabulate +dataclasses;python_version=="3.6.*" sqlalchemy +tabulate tqdm -dataclasses;python_version=="3.6.*" \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py index aa16a07..e3ef712 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -11,6 +11,7 @@ # Globals and constants variables. + @pytest.fixture def testdatadir(): - return os.path.join(os.path.dirname(__file__), 'testdata') \ No newline at end of file + return os.path.join(os.path.dirname(__file__), "testdata") diff --git a/tests/parser/test_campbell2001.py b/tests/parser/test_campbell2001.py index a9b3bcf..2f6f506 100644 --- a/tests/parser/test_campbell2001.py +++ b/tests/parser/test_campbell2001.py @@ -11,6 +11,7 @@ # Globals and constants variables. + def test_campbell2011(): parser = CampbellAtomicSubshellRadiativeWidthParser() diff --git a/tests/parser/test_dtsa.py b/tests/parser/test_dtsa.py index 2ffb29b..4117e5b 100644 --- a/tests/parser/test_dtsa.py +++ b/tests/parser/test_dtsa.py @@ -11,6 +11,7 @@ # Globals and constants variables. + def test_dtsa1992_subshell(): parser = DtsaSubshellParser() props = list(parser) @@ -23,6 +24,7 @@ def test_dtsa1992_subshell(): assert props[-1].element.z == 95 assert props[-1].value_eV == pytest.approx(366.49, abs=1e-2) + def test_dtsa1992_line(): parser = DtsaLineParser() props = list(parser) diff --git a/tests/parser/test_jeol.py b/tests/parser/test_jeol.py index 7541c3b..fe09720 100644 --- a/tests/parser/test_jeol.py +++ b/tests/parser/test_jeol.py @@ -10,6 +10,7 @@ # Globals and constants variables. + def test_jeol(): parser = JEOLTransitionParser() assert len(list(parser)) == 2372 diff --git a/tests/parser/test_nist.py b/tests/parser/test_nist.py index 733c2b6..cea1c88 100644 --- a/tests/parser/test_nist.py +++ b/tests/parser/test_nist.py @@ -11,6 +11,7 @@ # Globals and constants variables. + def test_coursey2015(): parser = NISTElementAtomicWeightParser() props = list(parser) diff --git a/tests/parser/test_notation.py b/tests/parser/test_notation.py index 6144978..d8af004 100644 --- a/tests/parser/test_notation.py +++ b/tests/parser/test_notation.py @@ -5,39 +5,56 @@ # Third party modules. # Local modules. -from pyxray.parser.notation import iter_subshells, ElementSymbolParser, AtomicShellNotationParser, AtomicSubshellNotationParser, GenericXrayTransitionNotationParser, KnownXrayTransitionNotationParser, SeriesXrayTransitionNotationParser, FamilyXrayTransitionNotationParser +from pyxray.parser.notation import ( + iter_subshells, + ElementSymbolParser, + AtomicShellNotationParser, + AtomicSubshellNotationParser, + GenericXrayTransitionNotationParser, + KnownXrayTransitionNotationParser, + SeriesXrayTransitionNotationParser, + FamilyXrayTransitionNotationParser, +) # Globals and constants variables. + def test_iter_subshells(): assert len(list(iter_subshells(1))) == 1 assert len(list(iter_subshells(2))) == 4 assert len(list(iter_subshells(7))) == 49 + def test_unattributed_symbol(): parser = ElementSymbolParser() assert len(list(parser)) == 118 + def test_atomicshell_notation(): parser = AtomicShellNotationParser() assert len(list(parser)) == 21 + def test_atomicsubshell_notation(): parser = AtomicSubshellNotationParser() assert len(list(parser)) == 147 + def test_generic_xray_transition_notation(): parser = GenericXrayTransitionNotationParser() assert len(list(parser)) == 1176 + def test_known_xray_transition_notation(): parser = KnownXrayTransitionNotationParser() assert len(list(parser)) == 76 + def test_series_xray_transition_notation(): parser = SeriesXrayTransitionNotationParser() assert len(list(parser)) == 14 + def test_family_xray_transition_notation(): parser = FamilyXrayTransitionNotationParser() - assert len(list(parser)) == 96 \ No newline at end of file + assert len(list(parser)) == 96 diff --git a/tests/parser/test_perkins1991.py b/tests/parser/test_perkins1991.py index a084a9c..3dd2274 100644 --- a/tests/parser/test_perkins1991.py +++ b/tests/parser/test_perkins1991.py @@ -7,8 +7,10 @@ # Local modules. from pyxray.parser.perkins1991 import Perkins1991Parser + # Globals and constants variables. + def test_perkins1991(): parser = Perkins1991Parser() assert len(list(parser)) == 19189 diff --git a/tests/parser/test_sargent_welch.py b/tests/parser/test_sargent_welch.py index b153fe4..4ed5d94 100644 --- a/tests/parser/test_sargent_welch.py +++ b/tests/parser/test_sargent_welch.py @@ -6,15 +6,19 @@ # Third party modules. # Local modules. -from pyxray.parser.sargent_welch import \ - SargentWelchElementAtomicWeightParser, SargentWelchElementMassDensityParser +from pyxray.parser.sargent_welch import ( + SargentWelchElementAtomicWeightParser, + SargentWelchElementMassDensityParser, +) # Globals and constants variables. + def test_sargentwelch2010_atomicweight(): parser = SargentWelchElementAtomicWeightParser() assert len(list(parser)) == 106 + def test_sargentwelch2010_massdensity(): parser = SargentWelchElementMassDensityParser() - assert len(list(parser)) == 94 \ No newline at end of file + assert len(list(parser)) == 94 diff --git a/tests/parser/test_wikipedia.py b/tests/parser/test_wikipedia.py index 45c0e79..09d7a2f 100644 --- a/tests/parser/test_wikipedia.py +++ b/tests/parser/test_wikipedia.py @@ -10,6 +10,7 @@ # Globals and constants variables. + def test_wikipedia(): parser = WikipediaElementNameParser() assert len(list(parser)) > 0 diff --git a/tests/sql/conftest.py b/tests/sql/conftest.py index 16a8f98..bb6685d 100644 --- a/tests/sql/conftest.py +++ b/tests/sql/conftest.py @@ -17,72 +17,95 @@ L3 = descriptor.AtomicSubshell(2, 1, 3) L2 = descriptor.AtomicSubshell(2, 1, 1) -class MockParser(_Parser): +class MockParser(_Parser): def __iter__(self): - reference = descriptor.Reference('lee1966') - reference2 = descriptor.Reference('doe2016') + reference = descriptor.Reference("lee1966") + reference2 = descriptor.Reference("doe2016") element = descriptor.Element(118) atomic_shell = descriptor.AtomicShell(1) transition = descriptor.XrayTransition(L3, K) transition2 = descriptor.XrayTransition(L2, K) transitionset = descriptor.XrayTransition(2, 1, None, K) - notation = descriptor.Notation('mock') - notation_iupac = descriptor.Notation('iupac') - language = descriptor.Language('en') + notation = descriptor.Notation("mock") + notation_iupac = descriptor.Notation("iupac") + language = descriptor.Language("en") - yield property.ElementName(reference, element, language, 'Vibranium') - yield property.ElementSymbol(reference, element, 'Vi') + yield property.ElementName(reference, element, language, "Vibranium") + yield property.ElementSymbol(reference, element, "Vi") yield property.ElementAtomicWeight(reference, element, 999.1) yield property.ElementAtomicWeight(reference2, element, 111.1) yield property.ElementMassDensity(reference, element, 999.2) - yield property.AtomicShellNotation(reference, atomic_shell, notation, 'a', 'b', 'c', 'd') + yield property.AtomicShellNotation( + reference, atomic_shell, notation, "a", "b", "c", "d" + ) - yield property.AtomicSubshellNotation(reference, K, notation, 'a', 'b', 'c', 'd') + yield property.AtomicSubshellNotation( + reference, K, notation, "a", "b", "c", "d" + ) yield property.AtomicSubshellBindingEnergy(reference, element, K, 0.1) yield property.AtomicSubshellRadiativeWidth(reference, element, K, 0.01) yield property.AtomicSubshellNonRadiativeWidth(reference, element, K, 0.001) yield property.AtomicSubshellOccupancy(reference, element, K, 1) - yield property.XrayTransitionNotation(reference, transition, notation, 'a', 'b', 'c', 'd') - yield property.XrayTransitionNotation(reference, transition, notation_iupac, 'aa', 'bb', 'cc', 'dd') + yield property.XrayTransitionNotation( + reference, transition, notation, "a", "b", "c", "d" + ) + yield property.XrayTransitionNotation( + reference, transition, notation_iupac, "aa", "bb", "cc", "dd" + ) yield property.XrayTransitionEnergy(reference, element, transition, 0.2) yield property.XrayTransitionProbability(reference, element, transition, 0.02) - yield property.XrayTransitionRelativeWeight(reference, element, transition, 0.002) + yield property.XrayTransitionRelativeWeight( + reference, element, transition, 0.002 + ) - yield property.XrayTransitionNotation(reference, transition2, notation, 'e', 'f', 'g', 'h') + yield property.XrayTransitionNotation( + reference, transition2, notation, "e", "f", "g", "h" + ) yield property.XrayTransitionEnergy(reference, element, transition2, 0.4) yield property.XrayTransitionProbability(reference, element, transition2, 0.04) - yield property.XrayTransitionRelativeWeight(reference, element, transition2, 0.004) + yield property.XrayTransitionRelativeWeight( + reference, element, transition2, 0.004 + ) - yield property.XrayTransitionNotation(reference, transitionset, notation, 'i', 'j', 'k', 'l') + yield property.XrayTransitionNotation( + reference, transitionset, notation, "i", "j", "k", "l" + ) yield property.XrayTransitionEnergy(reference, element, transitionset, 0.6) - yield property.XrayTransitionProbability(reference, element, transitionset, 0.06) - yield property.XrayTransitionRelativeWeight(reference, element, transitionset, 0.006) + yield property.XrayTransitionProbability( + reference, element, transitionset, 0.06 + ) + yield property.XrayTransitionRelativeWeight( + reference, element, transitionset, 0.006 + ) -class MockBadParser(_Parser): +class MockBadParser(_Parser): def __iter__(self): raise Exception -class MockSqliteDatabaseBuilder(SqlDatabaseBuilder): +class MockSqliteDatabaseBuilder(SqlDatabaseBuilder): def __init__(self, filepath=None, badparser=False): super().__init__(filepath) self.badparser = badparser def _find_parsers(self): - super()._find_parsers() # Ignore output, only for coverage + super()._find_parsers() # Ignore output, only for coverage if self.badparser: - return [('bad', MockBadParser())] + return [("bad", MockBadParser())] else: - return [('mock', MockParser())] + return [("mock", MockParser())] + -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def builder(tmp_path_factory): - engine = sqlalchemy.create_engine('sqlite:///' + str(tmp_path_factory.mktemp('test').joinpath('pyxray.sql'))) + engine = sqlalchemy.create_engine( + "sqlite:///" + str(tmp_path_factory.mktemp("test").joinpath("pyxray.sql")) + ) builder = MockSqliteDatabaseBuilder(engine) builder.build() diff --git a/tests/sql/test_build.py b/tests/sql/test_build.py index 6771729..d90258c 100644 --- a/tests/sql/test_build.py +++ b/tests/sql/test_build.py @@ -12,14 +12,16 @@ # Globals and constants variables. + def test_database_build(builder): assert os.path.exists(builder.engine.url.database) conn = sqlite3.connect(builder.engine.url.database) command = "SELECT count(*) FROM sqlite_master WHERE type = 'table'" - ntable, = conn.execute(command).fetchone() + (ntable,) = conn.execute(command).fetchone() assert ntable == 21 + def test_database_fail(builder): builder.badparser = True diff --git a/tests/sql/test_data.py b/tests/sql/test_data.py index 3928b26..98c7e4c 100644 --- a/tests/sql/test_data.py +++ b/tests/sql/test_data.py @@ -20,106 +20,136 @@ L3 = descriptor.AtomicSubshell(2, 1, 3) L2 = descriptor.AtomicSubshell(2, 1, 1) -@pytest.fixture(scope='session') + +@pytest.fixture(scope="session") def database(builder): return SqlDatabase(builder.engine) + @pytest.fixture def database_real(tmp_path): return pyxray.data.database + def test_add_preferred_reference(database): database.clear_preferred_references() - database.add_preferred_reference('lee1966') + database.add_preferred_reference("lee1966") assert len(database.get_preferred_references()) == 1 - assert 'lee1966' in database.get_preferred_references() + assert "lee1966" in database.get_preferred_references() database.clear_preferred_references() assert len(database.get_preferred_references()) == 0 + def test_add_preferred_reference_not_found(database): with pytest.raises(NotFound): - database.add_preferred_reference('foo') + database.add_preferred_reference("foo") + -@pytest.mark.parametrize('element', [118, 'Vi', 'Vibranium']) +@pytest.mark.parametrize("element", [118, "Vi", "Vibranium"]) def test_element(database, element): assert database.element(element) == descriptor.Element(118) -@pytest.mark.parametrize('reference', ['lee1966', 'LEE1966']) + +@pytest.mark.parametrize("reference", ["lee1966", "LEE1966"]) def test_reference(database, reference): - assert database.element_name(118, 'en', reference) == 'Vibranium' + assert database.element_name(118, "en", reference) == "Vibranium" -@pytest.mark.parametrize('element', ['Al', 13, 'Aluminum']) + +@pytest.mark.parametrize("element", ["Al", 13, "Aluminum"]) def test_element_notfound(database, element): with pytest.raises(NotFound): database.element(element) -@pytest.mark.parametrize('element', [118, 'Vi', 'Vibranium']) + +@pytest.mark.parametrize("element", [118, "Vi", "Vibranium"]) def test_element_atomic_number(database, element): assert database.element_atomic_number(element) == 118 -@pytest.mark.parametrize('element', ['Al', 13, 'Aluminum']) + +@pytest.mark.parametrize("element", ["Al", 13, "Aluminum"]) def test_element_atomic_number_notfound(database, element): with pytest.raises(NotFound): database.element_atomic_number(element) -@pytest.mark.parametrize('element', [118, 'Vi', 'Vibranium']) + +@pytest.mark.parametrize("element", [118, "Vi", "Vibranium"]) def test_element_symbol(database, element): - assert database.element_symbol(element) == 'Vi' + assert database.element_symbol(element) == "Vi" + -@pytest.mark.parametrize('element', ['Al', 13, 'Aluminum']) +@pytest.mark.parametrize("element", ["Al", 13, "Aluminum"]) def test_element_symbol_notfound(database, element): with pytest.raises(NotFound): database.element_symbol(element) -@pytest.mark.parametrize('element', [118, 'Vi', 'Vibranium']) + +@pytest.mark.parametrize("element", [118, "Vi", "Vibranium"]) def test_element_name(database, element): - assert database.element_name(element, 'en') == 'Vibranium' - assert database.element_name(element, 'en', 'lee1966') == 'Vibranium' + assert database.element_name(element, "en") == "Vibranium" + assert database.element_name(element, "en", "lee1966") == "Vibranium" + -@pytest.mark.parametrize('element', ['Al', 13, 'Aluminum']) +@pytest.mark.parametrize("element", ["Al", 13, "Aluminum"]) def test_element_name_notfound(database, element): with pytest.raises(NotFound): - database.element_name(element, 'en') + database.element_name(element, "en") + def test_element_name_notfound_wrong_language(database): with pytest.raises(NotFound): - database.element_name(118, 'fr') + database.element_name(118, "fr") + def test_element_name_notfound_wrong_reference(database): with pytest.raises(NotFound): - database.element_name(118, 'en', 'doe2016') + database.element_name(118, "en", "doe2016") + def test_element_atomic_weight_no_reference(database): assert database.element_atomic_weight(118) == pytest.approx(999.1, abs=1e-2) + def test_element_atomic_weight_lee1966(database): - assert database.element_atomic_weight(118, 'lee1966') == pytest.approx(999.1, abs=1e-2) + assert database.element_atomic_weight(118, "lee1966") == pytest.approx( + 999.1, abs=1e-2 + ) + def test_element_atomic_weight_doe2016(database): - assert database.element_atomic_weight(118, 'doe2016') == pytest.approx(111.1, abs=1e-2) + assert database.element_atomic_weight(118, "doe2016") == pytest.approx( + 111.1, abs=1e-2 + ) + def test_element_atomic_weight_preferred_reference(database): database.clear_preferred_references() - database.add_preferred_reference('doe2016') + database.add_preferred_reference("doe2016") assert database.element_atomic_weight(118) == pytest.approx(111.1, abs=1e-2) database.clear_preferred_references() - database.add_preferred_reference('lee1966') + database.add_preferred_reference("lee1966") assert database.element_atomic_weight(118) == pytest.approx(999.1, abs=1e-2) database.clear_preferred_references() -@pytest.mark.parametrize('element', [118, 'Vi', 'Vibranium']) + +@pytest.mark.parametrize("element", [118, "Vi", "Vibranium"]) def test_element_mass_density_kg_per_m3(database, element): - assert database.element_mass_density_kg_per_m3(element) == pytest.approx(999.2, abs=1e-2) + assert database.element_mass_density_kg_per_m3(element) == pytest.approx( + 999.2, abs=1e-2 + ) -@pytest.mark.parametrize('element', [118, 'Vi', 'Vibranium']) + +@pytest.mark.parametrize("element", [118, "Vi", "Vibranium"]) def test_element_mass_density_g_per_cm3(database, element): - assert database.element_mass_density_g_per_cm3(element) == pytest.approx(0.9992, abs=1e-4) + assert database.element_mass_density_g_per_cm3(element) == pytest.approx( + 0.9992, abs=1e-4 + ) + -@pytest.mark.parametrize('element,reference', [(118, None), (118, 'lee1966')]) +@pytest.mark.parametrize("element,reference", [(118, None), (118, "lee1966")]) def test_element_xray_transitions(database, element, reference): transitions = database.element_xray_transitions(element, reference=reference) assert len(transitions) == 3 @@ -128,110 +158,187 @@ def test_element_xray_transitions(database, element, reference): assert descriptor.XrayTransition(L2, K) in transitions assert descriptor.XrayTransition(2, 1, None, K) in transitions -@pytest.mark.parametrize('xray_transition,expected', [ + +@pytest.mark.parametrize( + "xray_transition,expected", + [ (descriptor.XrayTransition(L3, K), 1), - (descriptor.XrayTransition(2, 1, None, K), 2) -]) -def test_element_xray_transitions_with_xray_transition(database, xray_transition, expected): + (descriptor.XrayTransition(2, 1, None, K), 2), + ], +) +def test_element_xray_transitions_with_xray_transition( + database, xray_transition, expected +): transitions = database.element_xray_transitions(118, xray_transition) assert len(transitions) == expected -@pytest.mark.parametrize('element, expected', [ - (13, 14), - (6, 2), - (5, 3), - (4, 3), - (3, 2), -]) + +@pytest.mark.parametrize( + "element, expected", [(13, 14), (6, 2), (5, 3), (4, 3), (3, 2),] +) def test_element_xray_transitions(database_real, element, expected): transitions = database_real.element_xray_transitions(element) assert len(transitions) == expected -@pytest.mark.parametrize('element,reference', [(118, 'unknown'), (1, None)]) + +@pytest.mark.parametrize("element,reference", [(118, "unknown"), (1, None)]) def test_element_xray_transitions_notfound(database, element, reference): with pytest.raises(NotFound): database.element_xray_transitions(element, reference) + def test_element_xray_transition(database): - transition = database.element_xray_transition(118, 'a') - assert transition == descriptor.XrayTransition(L3, K) + transition = database.element_xray_transition(118, "a") + assert transition == descriptor.XrayTransition(L3, K) + -@pytest.mark.parametrize('element,reference', [(1, 'a'), (118, 'g')]) +@pytest.mark.parametrize("element,reference", [(1, "a"), (118, "g")]) def test_element_xray_transition_notfound(database, element, reference): with pytest.raises(NotFound): database.element_xray_transition(element, reference) -@pytest.mark.parametrize('atomic_shell', [1, 'a', 'b']) + +@pytest.mark.parametrize("atomic_shell", [1, "a", "b"]) def test_atomic_shell(database, atomic_shell): assert database.atomic_shell(atomic_shell) == descriptor.AtomicShell(1) -@pytest.mark.parametrize('atomic_shell', ['c', 3]) + +@pytest.mark.parametrize("atomic_shell", ["c", 3]) def test_atomic_shell_notfound(database, atomic_shell): with pytest.raises(NotFound): database.atomic_shell(atomic_shell) -@pytest.mark.parametrize('encoding,expected', [('ascii', 'a'), ('utf16', 'b'), ('html', 'c'), ('latex', 'd')]) + +@pytest.mark.parametrize( + "encoding,expected", [("ascii", "a"), ("utf16", "b"), ("html", "c"), ("latex", "d")] +) def test_atomic_shell_notation(database, encoding, expected): - assert database.atomic_shell_notation(1, 'mock', encoding) == expected + assert database.atomic_shell_notation(1, "mock", encoding) == expected + -@pytest.mark.parametrize('atomic_subshell', [descriptor.AtomicSubshell(1, 0, 1), (1, 0, 1), 'a', 'b']) +@pytest.mark.parametrize( + "atomic_subshell", [descriptor.AtomicSubshell(1, 0, 1), (1, 0, 1), "a", "b"] +) def test_atomic_subshell(database, atomic_subshell): - assert database.atomic_subshell(atomic_subshell) == descriptor.AtomicSubshell(1, 0, 1) + assert database.atomic_subshell(atomic_subshell) == descriptor.AtomicSubshell( + 1, 0, 1 + ) -@pytest.mark.parametrize('atomic_subshell', ['c', (3, 3, 3)]) + +@pytest.mark.parametrize("atomic_subshell", ["c", (3, 3, 3)]) def test_atomic_subshell_notfound(database, atomic_subshell): with pytest.raises(NotFound): database.atomic_subshell(atomic_subshell) -@pytest.mark.parametrize('encoding,expected', [('ascii', 'a'), ('utf16', 'b'), ('html', 'c'), ('latex', 'd')]) + +@pytest.mark.parametrize( + "encoding,expected", [("ascii", "a"), ("utf16", "b"), ("html", "c"), ("latex", "d")] +) def testatomic_subshell_notation(database, encoding, expected): - assert database.atomic_subshell_notation(descriptor.AtomicSubshell(1, 0, 1), 'mock', encoding) == expected + assert ( + database.atomic_subshell_notation( + descriptor.AtomicSubshell(1, 0, 1), "mock", encoding + ) + == expected + ) -@pytest.mark.parametrize('atomic_subshell', [descriptor.AtomicSubshell(1, 0, 1), (1, 0, 1), 'a', 'b']) + +@pytest.mark.parametrize( + "atomic_subshell", [descriptor.AtomicSubshell(1, 0, 1), (1, 0, 1), "a", "b"] +) def test_atomic_subshell_binding_energy_eV(database, atomic_subshell): - assert database.atomic_subshell_binding_energy_eV(118, atomic_subshell) == pytest.approx(0.1, abs=1e-4) + assert database.atomic_subshell_binding_energy_eV( + 118, atomic_subshell + ) == pytest.approx(0.1, abs=1e-4) + -@pytest.mark.parametrize('atomic_subshell', [descriptor.AtomicSubshell(1, 0, 1), (1, 0, 1), 'a', 'b']) +@pytest.mark.parametrize( + "atomic_subshell", [descriptor.AtomicSubshell(1, 0, 1), (1, 0, 1), "a", "b"] +) def testatomic_subshell_radiative_width_eV(database, atomic_subshell): - assert database.atomic_subshell_radiative_width_eV(118, atomic_subshell) == pytest.approx(0.01, abs=1e-4) + assert database.atomic_subshell_radiative_width_eV( + 118, atomic_subshell + ) == pytest.approx(0.01, abs=1e-4) + -@pytest.mark.parametrize('atomic_subshell', [descriptor.AtomicSubshell(1, 0, 1), (1, 0, 1), 'a', 'b']) +@pytest.mark.parametrize( + "atomic_subshell", [descriptor.AtomicSubshell(1, 0, 1), (1, 0, 1), "a", "b"] +) def testatomic_subshell_nonradiative_width_eV(database, atomic_subshell): - assert database.atomic_subshell_nonradiative_width_eV(118, atomic_subshell) == pytest.approx(0.001, abs=1e-4) + assert database.atomic_subshell_nonradiative_width_eV( + 118, atomic_subshell + ) == pytest.approx(0.001, abs=1e-4) -@pytest.mark.parametrize('atomic_subshell', [descriptor.AtomicSubshell(1, 0, 1), (1, 0, 1), 'a', 'b']) + +@pytest.mark.parametrize( + "atomic_subshell", [descriptor.AtomicSubshell(1, 0, 1), (1, 0, 1), "a", "b"] +) def testatomic_subshell_occupancy(database, atomic_subshell): assert database.atomic_subshell_occupancy(118, atomic_subshell) == 1 -@pytest.mark.parametrize('xray_transition', ['a', ((2, 1, 3), (1, 0, 1)), (L3, K), descriptor.XrayTransition(L3, K)]) + +@pytest.mark.parametrize( + "xray_transition", + ["a", ((2, 1, 3), (1, 0, 1)), (L3, K), descriptor.XrayTransition(L3, K)], +) def test_xray_transition(database, xray_transition): assert database.xray_transition(xray_transition) == descriptor.XrayTransition(L3, K) -@pytest.mark.parametrize('encoding,expected', [('ascii', 'a'), ('utf16', 'b'), ('html', 'c'), ('latex', 'd')]) + +@pytest.mark.parametrize( + "encoding,expected", [("ascii", "a"), ("utf16", "b"), ("html", "c"), ("latex", "d")] +) def test_xray_transition_notation(database, encoding, expected): xray_transition = descriptor.XrayTransition(L3, K) - assert database.xray_transition_notation(xray_transition, 'mock', encoding) == expected + assert ( + database.xray_transition_notation(xray_transition, "mock", encoding) == expected + ) -@pytest.mark.parametrize('xray_transition', ['a', ((2, 1, 3), (1, 0, 1)), (L3, K), descriptor.XrayTransition(L3, K)]) + +@pytest.mark.parametrize( + "xray_transition", + ["a", ((2, 1, 3), (1, 0, 1)), (L3, K), descriptor.XrayTransition(L3, K)], +) def test_xray_transition_energy_eV_KL3(database, xray_transition): - assert database.xray_transition_energy_eV(118, xray_transition) == pytest.approx(0.2, abs=1e-4) + assert database.xray_transition_energy_eV(118, xray_transition) == pytest.approx( + 0.2, abs=1e-4 + ) + -@pytest.mark.parametrize('xray_transition', ['e', ((2, 1, 1), (1, 0, 1)), (L2, K), descriptor.XrayTransition(L2, K)]) +@pytest.mark.parametrize( + "xray_transition", + ["e", ((2, 1, 1), (1, 0, 1)), (L2, K), descriptor.XrayTransition(L2, K)], +) def test_xray_transition_energy_eV_KL2(database, xray_transition): - assert database.xray_transition_energy_eV(118, xray_transition) == pytest.approx(0.4, abs=1e-4) + assert database.xray_transition_energy_eV(118, xray_transition) == pytest.approx( + 0.4, abs=1e-4 + ) + -@pytest.mark.parametrize('xray_transition', ['a', ((2, 1, 3), (1, 0, 1)), (L3, K), descriptor.XrayTransition(L3, K)]) +@pytest.mark.parametrize( + "xray_transition", + ["a", ((2, 1, 3), (1, 0, 1)), (L3, K), descriptor.XrayTransition(L3, K)], +) def test_xray_transition_probability(database, xray_transition): - assert database.xray_transition_probability(118, xray_transition) == pytest.approx(0.02, abs=1e-4) + assert database.xray_transition_probability(118, xray_transition) == pytest.approx( + 0.02, abs=1e-4 + ) -@pytest.mark.parametrize('xray_transition', ['a', ((2, 1, 3), (1, 0, 1)), (L3, K), descriptor.XrayTransition(L3, K)]) + +@pytest.mark.parametrize( + "xray_transition", + ["a", ((2, 1, 3), (1, 0, 1)), (L3, K), descriptor.XrayTransition(L3, K)], +) def test_xray_transition_relative_weight(database, xray_transition): - assert database.xray_transition_relative_weight(118, xray_transition) == pytest.approx(0.002, abs=1e-4) + assert database.xray_transition_relative_weight( + 118, xray_transition + ) == pytest.approx(0.002, abs=1e-4) + def test_xray_line(database): - xrayline = database.xray_line(118, 'aa') + xrayline = database.xray_line(118, "aa") - assert xrayline.element.atomic_number == 118 - assert xrayline.iupac == 'Vi bb' - assert xrayline.siegbahn == 'Vi bb' - assert xrayline.energy_eV == pytest.approx(0.2, abs=1e-3) \ No newline at end of file + assert xrayline.element.atomic_number == 118 + assert xrayline.iupac == "Vi bb" + assert xrayline.siegbahn == "Vi bb" + assert xrayline.energy_eV == pytest.approx(0.2, abs=1e-3) diff --git a/tests/test_base.py b/tests/test_base.py index 7eda071..833dd6b 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -11,8 +11,8 @@ # Globals and constants variables. -class MockDatabase(_DatabaseMixin): +class MockDatabase(_DatabaseMixin): def element(self, element): pass @@ -22,7 +22,7 @@ def element_atomic_number(self, element): def element_symbol(self, element, reference=None): pass - def element_name(self, element, language='en', reference=None): + def element_name(self, element, language="en", reference=None): pass def element_atomic_weight(self, element, reference=None): @@ -43,22 +43,32 @@ def element_xray_transition(self, element, xraytransitionset=None, reference=Non def atomic_shell(self, atomic_shell): pass - def atomic_shell_notation(self, atomic_shell, notation, encoding='utf16', reference=None): + def atomic_shell_notation( + self, atomic_shell, notation, encoding="utf16", reference=None + ): pass def atomic_subshell(self, atomic_subshell): pass - def atomic_subshell_notation(self, atomic_subshell, notation, encoding='utf16', reference=None): + def atomic_subshell_notation( + self, atomic_subshell, notation, encoding="utf16", reference=None + ): pass - def atomic_subshell_binding_energy_eV(self, element, atomic_subshell, reference=None): + def atomic_subshell_binding_energy_eV( + self, element, atomic_subshell, reference=None + ): pass - def atomic_subshell_radiative_width_eV(self, element, atomic_subshell, reference=None): + def atomic_subshell_radiative_width_eV( + self, element, atomic_subshell, reference=None + ): pass - def atomic_subshell_nonradiative_width_eV(self, element, atomic_subshell, reference=None): + def atomic_subshell_nonradiative_width_eV( + self, element, atomic_subshell, reference=None + ): pass def atomic_subshell_occupancy(self, element, atomic_subshell, reference=None): @@ -67,7 +77,9 @@ def atomic_subshell_occupancy(self, element, atomic_subshell, reference=None): def xray_transition(self, xraytransition): pass - def xray_transition_notation(self, xraytransition, notation, encoding='utf16', reference=None): + def xray_transition_notation( + self, xraytransition, notation, encoding="utf16", reference=None + ): pass def xray_transition_energy_eV(self, element, xraytransition, reference=None): @@ -82,22 +94,28 @@ def xray_transition_relative_weight(self, element, xraytransition, reference=Non def xray_transitionset(self, xraytransitionset): pass - def xray_transitionset_notation(self, xraytransitionset, notation, encoding='utf16', reference=None): + def xray_transitionset_notation( + self, xraytransitionset, notation, encoding="utf16", reference=None + ): pass def xray_transitionset_energy_eV(self, element, xraytransitionset, reference=None): pass - def xray_transitionset_relative_weight(self, element, xraytransitionset, reference=None): + def xray_transitionset_relative_weight( + self, element, xraytransitionset, reference=None + ): pass def xray_line(self, element, line, reference=None): pass + @pytest.fixture def database(): return MockDatabase() + # def test_base_get_default_reference(database): # assert database.get_default_reference('element_symbol') is None @@ -111,4 +129,3 @@ def database(): # with pytest.raises(ValueError): # database.set_default_reference('foo', 'doe2016') - diff --git a/tests/test_cbook.py b/tests/test_cbook.py index 0db9863..be71516 100644 --- a/tests/test_cbook.py +++ b/tests/test_cbook.py @@ -11,26 +11,32 @@ # Globals and constants variables. + class MockProgress(ProgressMixin): pass + class MockProgressReport(ProgressReportMixin): pass + @pytest.fixture def progress(): return MockProgress() + def test_progress_update(progress): progress.update(50) assert progress.progress == 50 + @pytest.fixture def progress_report(progress): report = MockProgressReport() report.add_reporthook(lambda p: progress.update(p)) return report + def test_progress_report_update(progress_report, progress): assert progress.progress == 0 @@ -38,4 +44,3 @@ def test_progress_report_update(progress_report, progress): assert progress_report.progress == 50 assert progress.progress == 50 - diff --git a/tests/test_composition.py b/tests/test_composition.py index 43c7bc9..5230118 100644 --- a/tests/test_composition.py +++ b/tests/test_composition.py @@ -9,13 +9,19 @@ # Local modules. import pyxray -from pyxray.composition import Composition, convert_atomic_to_mass_fractions, convert_mass_to_atomic_fractions +from pyxray.composition import ( + Composition, + convert_atomic_to_mass_fractions, + convert_mass_to_atomic_fractions, +) # Globals and constants variables. + @pytest.fixture def al2o3(): - return Composition.from_formula('Al2O3') + return Composition.from_formula("Al2O3") + def test_convert_al2o3(al2o3): atomic_fractions = al2o3.atomic_fractions @@ -28,7 +34,8 @@ def test_convert_al2o3(al2o3): assert atomic_fractions2[13] == pytest.approx(atomic_fractions2[13], 1e-4) assert atomic_fractions2[8] == pytest.approx(atomic_fractions2[8], 1e-4) -@pytest.mark.parametrize('z', [5, 26, 92]) + +@pytest.mark.parametrize("z", [5, 26, 92]) def test_from_pure(z): comp = Composition.from_pure(z) @@ -37,7 +44,8 @@ def test_from_pure(z): assert comp.formula == pyxray.element_symbol(z) assert comp.is_normalized() -@pytest.mark.parametrize('formula', ['Al2Na3B12', 'Al2 Na3 B12', 'Al2.0 Na3.0 B12.0']) + +@pytest.mark.parametrize("formula", ["Al2Na3B12", "Al2 Na3 B12", "Al2.0 Na3.0 B12.0"]) def test_from_formula(formula): comp = Composition.from_formula(formula) @@ -49,28 +57,32 @@ def test_from_formula(formula): assert comp.atomic_fractions[11] == pytest.approx(3 / 17, 1e-4) assert comp.atomic_fractions[5] == pytest.approx(12 / 17, 1e-4) - assert comp.formula == 'Al2Na3B12' + assert comp.formula == "Al2Na3B12" assert comp.is_normalized() -@pytest.mark.parametrize('formula', ['Al', 'Al2', 'Al3.0']) + +@pytest.mark.parametrize("formula", ["Al", "Al2", "Al3.0"]) def test_from_formula_pure(formula): comp = Composition.from_formula(formula) assert comp.mass_fractions[13] == pytest.approx(1.0, 1e-4) assert comp.atomic_fractions[13] == pytest.approx(1.0, 1e-4) - assert comp.formula == 'Al' + assert comp.formula == "Al" assert comp.is_normalized() -@pytest.mark.parametrize('z', range(1, 100)) + +@pytest.mark.parametrize("z", range(1, 100)) def test_from_formula_symbol(z): formula = pyxray.element_symbol(z) comp = Composition.from_formula(formula) assert comp.formula == formula + def test_from_formula_exception(): with pytest.raises(Exception): - Composition.from_formula('Aq2 Na3 B12') + Composition.from_formula("Aq2 Na3 B12") + def test_from_massfractions(): comp = Composition.from_mass_fractions({13: 0.52925, 8: 0.47075}) @@ -83,6 +95,7 @@ def test_from_massfractions(): assert comp.is_normalized() + def test_from_massfractions_unnormalized(): comp = Composition.from_mass_fractions({13: 0.52925, 8: 0.3}) @@ -94,8 +107,9 @@ def test_from_massfractions_unnormalized(): assert not comp.is_normalized() + def test_from_massfractions_wildcard(): - comp = Composition.from_mass_fractions({13: 0.52925, 8: '?'}) + comp = Composition.from_mass_fractions({13: 0.52925, 8: "?"}) assert comp.mass_fractions[13] == pytest.approx(0.52925, 1e-4) assert comp.mass_fractions[8] == pytest.approx(0.47075, 1e-4) @@ -105,52 +119,67 @@ def test_from_massfractions_wildcard(): assert comp.is_normalized() + def test_eq(al2o3): assert al2o3 == Composition.from_atomic_fractions({13: 0.4, 8: 0.6}) + def test_ne(al2o3): assert al2o3 != Composition.from_mass_fractions({13: 0.52925, 8: 0.47075}) + def test_hash1(al2o3): assert hash(al2o3) == hash(Composition.from_atomic_fractions({13: 0.4, 8: 0.6})) + def test_hash2(al2o3): - assert hash(al2o3) != hash(Composition.from_mass_fractions({13: 0.52925, 8: 0.47075})) + assert hash(al2o3) != hash( + Composition.from_mass_fractions({13: 0.52925, 8: 0.47075}) + ) + def test_mass_fractions(al2o3): with pytest.raises(TypeError): al2o3.mass_fractions[13] = 0.5 + def test_atomic_fractions(al2o3): with pytest.raises(TypeError): al2o3.atomic_fractions[13] = 0.5 + def test_formula(al2o3): - assert al2o3.formula == 'Al2O3' + assert al2o3.formula == "Al2O3" comp = Composition.from_atomic_fractions(al2o3.atomic_fractions) - assert comp.formula == 'Al2O3' + assert comp.formula == "Al2O3" comp = Composition.from_mass_fractions(al2o3.mass_fractions) - assert comp.formula == 'Al2O3' + assert comp.formula == "Al2O3" + def test__contains__(al2o3): assert 13 in al2o3 assert 8 in al2o3 + def test_inner_repr(al2o3): - assert 'Al: 0.5293, O: 0.4707' == al2o3.inner_repr() + assert "Al: 0.5293, O: 0.4707" == al2o3.inner_repr() + def test__init__(): with pytest.raises(TypeError): - Composition(None, {}, {}, 'foo') + Composition(None, {}, {}, "foo") + def test_pickle(al2o3): s = pickle.dumps(al2o3) assert al2o3 == pickle.loads(s) + def test_copy(al2o3): assert al2o3 == copy.copy(al2o3) + def test_deepcopy(al2o3): assert al2o3 == copy.deepcopy(al2o3) diff --git a/tests/test_descriptor.py b/tests/test_descriptor.py index 5a46faa..c009610 100644 --- a/tests/test_descriptor.py +++ b/tests/test_descriptor.py @@ -8,26 +8,41 @@ import pytest # Local modules. -from pyxray.descriptor import Element, AtomicShell, AtomicSubshell, Reference, XrayLine, XrayTransition, Language, Notation +from pyxray.descriptor import ( + Element, + AtomicShell, + AtomicSubshell, + Reference, + XrayLine, + XrayTransition, + Language, + Notation, +) # Globals and constants variables. + @pytest.fixture def element(): return Element(6) + def test_element(element): assert element.z == 6 assert element.atomic_number == 6 + def test_element_eq(element): assert element == Element(6) + def test_element_hash(element): assert hash(element) == hash(Element(6)) + def test_element_repr(element): - assert repr(element) == 'Element(z=6)' + assert repr(element) == "Element(z=6)" + def test_element_validate(): with pytest.raises(ValueError): @@ -36,6 +51,7 @@ def test_element_validate(): with pytest.raises(ValueError): Element(119) + def test_element_frozen(element): with pytest.raises(dataclasses.FrozenInstanceError): element.atomic_number = 7 @@ -46,27 +62,34 @@ def test_element_frozen(element): with pytest.raises(dataclasses.FrozenInstanceError): element.abc = 7 + @pytest.fixture def atomicshell(): return AtomicShell(3) + def test_atomicshell(atomicshell): assert atomicshell.n == 3 assert atomicshell.principal_quantum_number == 3 + def test_atomicshell_eq(atomicshell): assert atomicshell == AtomicShell(3) + def test_atomicshell_hash(atomicshell): assert hash(atomicshell) == hash(AtomicShell(3)) + def test_atomicshell_repr(atomicshell): - assert repr(atomicshell) == 'AtomicShell(n=3)' + assert repr(atomicshell) == "AtomicShell(n=3)" + def test_atomicshell_validable(): with pytest.raises(ValueError): AtomicShell(0) + def test_atomicshell_frozen(atomicshell): with pytest.raises(dataclasses.FrozenInstanceError): atomicshell.n = 7 @@ -77,10 +100,12 @@ def test_atomicshell_frozen(atomicshell): with pytest.raises(dataclasses.FrozenInstanceError): atomicshell.abc = 7 + @pytest.fixture def atomicsubshell(): return AtomicSubshell(3, 0, 1) + def test_atomicsubshell(atomicsubshell): assert atomicsubshell.n == 3 assert atomicsubshell.atomic_shell == AtomicShell(3) @@ -92,16 +117,20 @@ def test_atomicsubshell(atomicsubshell): assert atomicsubshell.j == 0.5 assert atomicsubshell.total_angular_momentum == 0.5 + def test_atomicsubshell_eq(atomicsubshell): assert atomicsubshell == AtomicSubshell(3, 0, 1) assert atomicsubshell == AtomicSubshell(AtomicShell(3), 0, 1) + def test_atomicsubshell_hash(atomicsubshell): assert hash(atomicsubshell) == hash(AtomicSubshell(3, 0, 1)) assert hash(atomicsubshell) == hash(AtomicSubshell(AtomicShell(3), 0, 1)) + def test_atomicsubshell_repr(atomicsubshell): - assert repr(atomicsubshell) == 'AtomicSubshell(n=3, l=0, j=0.5)' + assert repr(atomicsubshell) == "AtomicSubshell(n=3, l=0, j=0.5)" + def test_atomicsubshell_validate(): with pytest.raises(ValueError): @@ -113,6 +142,7 @@ def test_atomicsubshell_validate(): with pytest.raises(ValueError): AtomicSubshell(3, 3, 1) + def test_atomicsubshell_frozen(atomicsubshell): with pytest.raises(dataclasses.FrozenInstanceError): atomicsubshell.n = 7 @@ -123,14 +153,20 @@ def test_atomicsubshell_frozen(atomicsubshell): with pytest.raises(dataclasses.FrozenInstanceError): atomicsubshell.abc = 7 -@pytest.fixture(params=[(AtomicSubshell(2, 0, 1), AtomicSubshell(1, 0, 1)), - (2, 0, 1, 1, 0, 1), - ((2, 0, 1), (1, 0, 1)), - (AtomicSubshell(2, 0, 1), 1, 0, 1), - (2, 0, 1, AtomicSubshell(1, 0, 1))]) + +@pytest.fixture( + params=[ + (AtomicSubshell(2, 0, 1), AtomicSubshell(1, 0, 1)), + (2, 0, 1, 1, 0, 1), + ((2, 0, 1), (1, 0, 1)), + (AtomicSubshell(2, 0, 1), 1, 0, 1), + (2, 0, 1, AtomicSubshell(1, 0, 1)), + ] +) def xraytransition(request): return XrayTransition(*request.param) + def test_xraytransition(xraytransition): assert xraytransition.source_principal_quantum_number == 2 assert xraytransition.source_azimuthal_quantum_number == 0 @@ -146,15 +182,23 @@ def test_xraytransition(xraytransition): assert xraytransition.destination_subshell.l == 0 assert xraytransition.destination_subshell.j_n == 1 + def test_xraytransition_eq(xraytransition): assert xraytransition == XrayTransition((2, 0, 1), (1, 0, 1)) + def test_xraytransition_hash(xraytransition): assert hash(xraytransition) == hash(XrayTransition((2, 0, 1), (1, 0, 1))) - assert hash(xraytransition) == hash(XrayTransition(AtomicSubshell(2, 0, 1), AtomicSubshell(1, 0, 1))) + assert hash(xraytransition) == hash( + XrayTransition(AtomicSubshell(2, 0, 1), AtomicSubshell(1, 0, 1)) + ) + def test_xraytransition_repr(xraytransition): - assert repr(xraytransition) == 'XrayTransition([n=2, l=0, j=0.5] -> [n=1, l=0, j=0.5])' + assert ( + repr(xraytransition) == "XrayTransition([n=2, l=0, j=0.5] -> [n=1, l=0, j=0.5])" + ) + def test_xraytransition_frozen(xraytransition): with pytest.raises(dataclasses.FrozenInstanceError): @@ -166,9 +210,13 @@ def test_xraytransition_frozen(xraytransition): with pytest.raises(dataclasses.FrozenInstanceError): xraytransition.abc = 7 + @pytest.fixture def xraytransitionset(): - return XrayTransition(2, 1, None, 1, 0, 1) # L2 (2, 1, 0.5) and L3 (2, 1, 1.5) to K (1, 0, 0.5) + return XrayTransition( + 2, 1, None, 1, 0, 1 + ) # L2 (2, 1, 0.5) and L3 (2, 1, 1.5) to K (1, 0, 0.5) + def test_xraytransitionset(xraytransitionset): assert xraytransitionset.source_subshell.n == 2 @@ -178,45 +226,66 @@ def test_xraytransitionset(xraytransitionset): assert xraytransitionset.destination_subshell.l == 0 assert xraytransitionset.destination_subshell.j_n == 1 + def test_xraytransitionset_repr(xraytransitionset): - assert repr(xraytransitionset) == 'XrayTransition([n=2, l=1, j=*] -> [n=1, l=0, j=0.5])' + assert ( + repr(xraytransitionset) + == "XrayTransition([n=2, l=1, j=*] -> [n=1, l=0, j=0.5])" + ) + @pytest.fixture def xrayline(): - return XrayLine(Element(118), XrayTransition(2, 0, 1, 1, 0, 1), 'a', 'b', 0.1, 0.2, 0.3) + return XrayLine( + Element(118), XrayTransition(2, 0, 1, 1, 0, 1), "a", "b", 0.1, 0.2, 0.3 + ) + def test_xrayline(xrayline): assert xrayline.element.z == 118 assert xrayline.transition == XrayTransition(2, 0, 1, 1, 0, 1) - assert xrayline.iupac == 'a' - assert xrayline.siegbahn == 'b' + assert xrayline.iupac == "a" + assert xrayline.siegbahn == "b" assert xrayline.energy_eV == pytest.approx(0.1, abs=1e-4) assert xrayline.probability == pytest.approx(0.2, abs=1e-4) assert xrayline.relative_weight == pytest.approx(0.3, abs=1e-4) + def test_xrayline_eq(xrayline): - assert xrayline == XrayLine(118, XrayTransition(2, 0, 1, 1, 0, 1), 'a', 'b', 0.1) - assert xrayline == XrayLine(118, XrayTransition(2, 0, 1, 1, 0, 1), 'z', 'b', 0.1) - assert xrayline == XrayLine(118, XrayTransition(2, 0, 1, 1, 0, 1), 'a', 'z', 0.1) - assert xrayline == XrayLine(118, XrayTransition(2, 0, 1, 1, 0, 1), 'a', 'b', 99.0) - assert xrayline == XrayLine(118, XrayTransition(2, 0, 1, 1, 0, 1), 'a', 'b', 0.1, 99.0) - assert xrayline == XrayLine(118, XrayTransition(2, 0, 1, 1, 0, 1), 'a', 'b', 0.1, 0.2, 99.0) - assert xrayline == XrayLine(118, XrayTransition(2, 0, 1, 1, 0, 1), 'a', 'b', 0.1, 0.2, 0.3) - - assert xrayline != XrayLine(117, XrayTransition(2, 0, 1, 1, 0, 1), 'a', 'b', 0.1) - assert xrayline != XrayLine(118, XrayTransition(3, 0, 1, 1, 0, 1), 'a', 'b', 0.1) - -@pytest.mark.parametrize('other', [ - XrayLine(118, XrayTransition(2, 0, 1, 1, 0, 1), 'a', 'b', 0.1), - XrayLine(118, XrayTransition(2, 0, 1, 1, 0, 1), 'z', 'b', 0.1), - XrayLine(118, XrayTransition(2, 0, 1, 1, 0, 1), 'a', 'z', 0.1), - XrayLine(118, XrayTransition(2, 0, 1, 1, 0, 1), 'a', 'b', 0.2) -]) + assert xrayline == XrayLine(118, XrayTransition(2, 0, 1, 1, 0, 1), "a", "b", 0.1) + assert xrayline == XrayLine(118, XrayTransition(2, 0, 1, 1, 0, 1), "z", "b", 0.1) + assert xrayline == XrayLine(118, XrayTransition(2, 0, 1, 1, 0, 1), "a", "z", 0.1) + assert xrayline == XrayLine(118, XrayTransition(2, 0, 1, 1, 0, 1), "a", "b", 99.0) + assert xrayline == XrayLine( + 118, XrayTransition(2, 0, 1, 1, 0, 1), "a", "b", 0.1, 99.0 + ) + assert xrayline == XrayLine( + 118, XrayTransition(2, 0, 1, 1, 0, 1), "a", "b", 0.1, 0.2, 99.0 + ) + assert xrayline == XrayLine( + 118, XrayTransition(2, 0, 1, 1, 0, 1), "a", "b", 0.1, 0.2, 0.3 + ) + + assert xrayline != XrayLine(117, XrayTransition(2, 0, 1, 1, 0, 1), "a", "b", 0.1) + assert xrayline != XrayLine(118, XrayTransition(3, 0, 1, 1, 0, 1), "a", "b", 0.1) + + +@pytest.mark.parametrize( + "other", + [ + XrayLine(118, XrayTransition(2, 0, 1, 1, 0, 1), "a", "b", 0.1), + XrayLine(118, XrayTransition(2, 0, 1, 1, 0, 1), "z", "b", 0.1), + XrayLine(118, XrayTransition(2, 0, 1, 1, 0, 1), "a", "z", 0.1), + XrayLine(118, XrayTransition(2, 0, 1, 1, 0, 1), "a", "b", 0.2), + ], +) def test_xrayline_hash(xrayline, other): assert hash(xrayline) == hash(other) + def test_xrayline_repr(xrayline): - assert repr(xrayline) == 'XrayLine(a)' + assert repr(xrayline) == "XrayLine(a)" + def test_xrayline_frozen(xrayline): with pytest.raises(dataclasses.FrozenInstanceError): @@ -228,34 +297,41 @@ def test_xrayline_frozen(xrayline): with pytest.raises(dataclasses.FrozenInstanceError): xrayline.abc = 7 + @pytest.fixture def language(): - return Language('en') + return Language("en") + def test_language(language): - assert language.key == 'en' + assert language.key == "en" + def test_language_eq(language): - assert language == Language('en') - assert language == Language('EN') - assert language != Language('fr') + assert language == Language("en") + assert language == Language("EN") + assert language != Language("fr") + def test_language_hash(language): - assert hash(language) == hash(Language('EN')) + assert hash(language) == hash(Language("EN")) + def test_language_repr(language): - assert repr(language) == 'Language(en)' + assert repr(language) == "Language(en)" + def test_language_validate(): with pytest.raises(ValueError): - Language('english') + Language("english") with pytest.raises(ValueError): - Language('e') + Language("e") + def test_language_frozen(language): with pytest.raises(dataclasses.FrozenInstanceError): - language.key = 'fr' + language.key = "fr" with pytest.raises(dataclasses.FrozenInstanceError): del language.key @@ -263,31 +339,38 @@ def test_language_frozen(language): with pytest.raises(dataclasses.FrozenInstanceError): language.abc = 7 + @pytest.fixture def notation(): - return Notation('foo') + return Notation("foo") + def test_notation(notation): - assert notation.key == 'foo' + assert notation.key == "foo" + def test_notation_eq(notation): - assert notation == Notation('foo') - assert notation == Notation('FOO') - assert notation != Notation('bar') + assert notation == Notation("foo") + assert notation == Notation("FOO") + assert notation != Notation("bar") + def test_notation_hash(notation): - assert hash(notation) == hash(Notation('FOO')) + assert hash(notation) == hash(Notation("FOO")) + def test_notation_repr(notation): - assert repr(notation) == 'Notation(foo)' + assert repr(notation) == "Notation(foo)" + def test_notation_validate(): with pytest.raises(ValueError): - Notation('') + Notation("") + def test_notation_frozen(notation): with pytest.raises(dataclasses.FrozenInstanceError): - notation.key = 'bar' + notation.key = "bar" with pytest.raises(dataclasses.FrozenInstanceError): del notation.key @@ -295,26 +378,32 @@ def test_notation_frozen(notation): with pytest.raises(dataclasses.FrozenInstanceError): notation.abc = 7 + @pytest.fixture def reference(): - return Reference('doe2016') + return Reference("doe2016") + def test_reference(reference): - assert reference.bibtexkey == 'doe2016' + assert reference.bibtexkey == "doe2016" + def test_reference_eq(reference): - assert reference == Reference('doe2016') - assert reference != Reference('doe2016', year=2016) + assert reference == Reference("doe2016") + assert reference != Reference("doe2016", year=2016) + def test_reference_hash(reference): - assert hash(reference) == hash(Reference('doe2016')) + assert hash(reference) == hash(Reference("doe2016")) + def test_reference_repr(reference): - assert repr(reference) == 'Reference(doe2016)' + assert repr(reference) == "Reference(doe2016)" + def test_reference_frozen(reference): with pytest.raises(dataclasses.FrozenInstanceError): - reference.author = 'bar' + reference.author = "bar" with pytest.raises(dataclasses.FrozenInstanceError): del reference.author diff --git a/tests/test_epq.py b/tests/test_epq.py index 5f97026..e9ae51f 100644 --- a/tests/test_epq.py +++ b/tests/test_epq.py @@ -10,7 +10,10 @@ import sqlalchemy # Local modules. -from pyxray.parser.notation import AtomicSubshellNotationParser, KnownXrayTransitionNotationParser +from pyxray.parser.notation import ( + AtomicSubshellNotationParser, + KnownXrayTransitionNotationParser, +) from pyxray.sql.build import SqlDatabaseBuilder from pyxray.sql.data import SqlDatabase import pyxray.descriptor as descriptor @@ -18,48 +21,58 @@ # Globals and constants variables. -class EpqDatabaseBuilder(SqlDatabaseBuilder): +class EpqDatabaseBuilder(SqlDatabaseBuilder): def _find_parsers(self): - return [('atomic subshell notation', AtomicSubshellNotationParser()), - ('xray transition notation', KnownXrayTransitionNotationParser())] + return [ + ("atomic subshell notation", AtomicSubshellNotationParser()), + ("xray transition notation", KnownXrayTransitionNotationParser()), + ] + @pytest.fixture def database(tmp_path): - engine = sqlalchemy.create_engine('sqlite:///' + str(tmp_path.joinpath('epq.sql'))) + engine = sqlalchemy.create_engine("sqlite:///" + str(tmp_path.joinpath("epq.sql"))) builder = EpqDatabaseBuilder(engine) builder.build() return SqlDatabase(engine) + def test_epq_atomicsubshell_notation(database, testdatadir): - filepath = os.path.join(testdatadir, 'epq_atomicsubshell.csv') - with open(filepath, 'r') as fp: + filepath = os.path.join(testdatadir, "epq_atomicsubshell.csv") + with open(filepath, "r") as fp: reader = csv.DictReader(fp) for row in reader: - atomic_subshell = descriptor.AtomicSubshell(int(row['n']), int(row['l']), int(float(row['j']) * 2)) - assert database.atomic_subshell_notation(atomic_subshell, 'siegbahn') == row['notation'] + atomic_subshell = descriptor.AtomicSubshell( + int(row["n"]), int(row["l"]), int(float(row["j"]) * 2) + ) + assert ( + database.atomic_subshell_notation(atomic_subshell, "siegbahn") + == row["notation"] + ) + def test_epq_xraytransition_notation(database, testdatadir): - filepath = os.path.join(testdatadir, 'epq_xraytransition.csv') - with open(filepath, 'r') as fp: + filepath = os.path.join(testdatadir, "epq_xraytransition.csv") + with open(filepath, "r") as fp: reader = csv.DictReader(fp) for row in reader: - source = database.atomic_subshell(row['source']) - destination = database.atomic_subshell(row['destination']) + source = database.atomic_subshell(row["source"]) + destination = database.atomic_subshell(row["destination"]) transition = descriptor.XrayTransition(source, destination) - expected = ast.literal_eval('"' + row['notation'] + '"') - expected = expected.replace('p', "\u2032") + expected = ast.literal_eval('"' + row["notation"] + '"') + expected = expected.replace("p", "\u2032") try: - actual = database.xray_transition_notation(transition, 'siegbahn') + actual = database.xray_transition_notation(transition, "siegbahn") except NotFound: continue - actual = actual.strip('I') + actual = actual.strip("I") - assert actual == expected \ No newline at end of file + assert actual == expected diff --git a/tests/test_property.py b/tests/test_property.py index 43a79c8..6ad45a1 100644 --- a/tests/test_property.py +++ b/tests/test_property.py @@ -12,22 +12,25 @@ # Globals and constants variables. + @pytest.fixture def reference(): - return Reference('test2016') + return Reference("test2016") + @pytest.fixture def element_symbol(reference): - return ElementSymbol(reference, Element(6), 'C') + return ElementSymbol(reference, Element(6), "C") + def test_element_symbol(element_symbol, reference): assert element_symbol.reference == reference assert element_symbol.element == Element(6) - assert element_symbol.value == 'C' + assert element_symbol.value == "C" + def test_element_symbol_validate(reference): with pytest.raises(ValueError): - ElementSymbol(reference, Element(6), '') - ElementSymbol(reference, Element(6), 'CCC') - ElementSymbol(reference, Element(6), 'c') - + ElementSymbol(reference, Element(6), "") + ElementSymbol(reference, Element(6), "CCC") + ElementSymbol(reference, Element(6), "c") From 2a36ff6d66ee6581fdb84010736abfd11145d7cc Mon Sep 17 00:00:00 2001 From: Philippe Pinard Date: Sun, 10 May 2020 11:47:40 +0100 Subject: [PATCH 4/8] Add codecov configuration file --- codecov.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 codecov.yml diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..72484eb --- /dev/null +++ b/codecov.yml @@ -0,0 +1,12 @@ +coverage: + precision: 0 + round: down + range: "70...100" + status: + project: + default: + target: auto + threshold: 5% + base: auto + comment: + layout: "diff" From 63329d32a22d9b66c3448f5f8b0aa1b4fffef6ef Mon Sep 17 00:00:00 2001 From: Philippe Pinard Date: Sun, 10 May 2020 11:54:10 +0100 Subject: [PATCH 5/8] Fix invalid configuration --- codecov.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codecov.yml b/codecov.yml index 72484eb..a356d73 100644 --- a/codecov.yml +++ b/codecov.yml @@ -8,5 +8,5 @@ coverage: target: auto threshold: 5% base: auto - comment: - layout: "diff" +comment: + layout: "diff" From f6c1b62ff997397c98412aa17694c277abd1513d Mon Sep 17 00:00:00 2001 From: Philippe Pinard Date: Sun, 10 May 2020 11:59:44 +0100 Subject: [PATCH 6/8] Add target to CI badge --- README.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/README.rst b/README.rst index 0eebe54..f035b4a 100644 --- a/README.rst +++ b/README.rst @@ -6,6 +6,7 @@ pyxray :target: https://pypi.python.org/pypi/pyxray .. image:: https://img.shields.io/github/workflow/status/openmicroanalysis/pyxray/CI + :target: https://github.com/openmicroanalysis/pyxray/actions :alt: GitHub Workflow Status .. image:: https://img.shields.io/codecov/c/github/openmicroanalysis/pyxray.svg From 00ada96c02a8d8501b92c1109cdc7088e34da6fd Mon Sep 17 00:00:00 2001 From: Philippe Pinard Date: Sun, 10 May 2020 12:00:26 +0100 Subject: [PATCH 7/8] Add pre-commit installation --- README.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/README.rst b/README.rst index f035b4a..52943fc 100644 --- a/README.rst +++ b/README.rst @@ -35,6 +35,7 @@ For development installation from the git repository:: git clone git@github.com/openmicroanalysis/pyxray.git cd pyxray pip install -e . + pre-commit install See development section below From fe191b6aacd1f43c039cc433db9088f7d30442e3 Mon Sep 17 00:00:00 2001 From: Philippe Pinard Date: Sun, 10 May 2020 12:02:27 +0100 Subject: [PATCH 8/8] Add missing sphinx --- requirements-dev.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements-dev.txt b/requirements-dev.txt index 996df50..79a896d 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,2 +1,3 @@ requests requests-cache +sphinx