Skip to content

Commit

Permalink
Release/1.0.0b19 (#221)
Browse files Browse the repository at this point in the history
  • Loading branch information
yakutovicha committed Jul 22, 2021
2 parents cbc6a25 + 6f57b18 commit 6fa5f86
Show file tree
Hide file tree
Showing 17 changed files with 439 additions and 111 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:

strategy:
matrix:
tag: [ stable, latest ]
tag: [ stable ]
browser: [ chrome, firefox ]
fail-fast: false

Expand Down
86 changes: 86 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
name: Publish on Test PyPI and PyPI

on:
push:
branches:
# Commits pushed to release/ branches are published on Test PyPI if they
# have a new version number.
- "release/**"
tags:
# Tags that start with the "v" prefix are published on PyPI.
- "v*"

jobs:

build-and-publish-test:

name: Build and publish on TestPyPI
if: startsWith(github.ref, 'refs/heads/release/')

runs-on: ubuntu-latest
environment:
name: Test PyPI
url: https://test.pypi.org/project/aiidalab-widgets-base/

steps:
- uses: actions/checkout@v2

- name: Set up Python 3.8
uses: actions/setup-python@v2
with:
python-version: 3.8

- name: Install pypa/build
run: python -m pip install build

- name: Build a binary wheel and a source tarball
run: >-
python -m
build
--sdist
--wheel
--outdir dist/
.
- name: Publish distribution on Test PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
repository_url: https://test.pypi.org/legacy/

build-and-publish:

name: Build and publish on PyPI
if: startsWith(github.ref, 'refs/tags')

runs-on: ubuntu-latest
environment:
name: PyPI
url: https://pypi.org/project/aiidalab-widgets-base/

steps:
- uses: actions/checkout@v2

- name: Set up Python 3.8
uses: actions/setup-python@v2
with:
python-version: 3.8

- name: Install pypa/build
run: python -m pip install build

- name: Build a binary wheel and a source tarball
run: >-
python -m
build
--sdist
--wheel
--outdir dist/
.
- name: Publish distribution on PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
6 changes: 6 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,9 @@ repos:
hooks:
- id: flake8
args: [--count, --show-source, --statistics]

- repo: https://github.com/pycqa/isort
rev: 5.6.4
hooks:
- id: isort
args: ["--profile", "black", "--filter-files"]
25 changes: 10 additions & 15 deletions aiidalab_widgets_base/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@

load_profile()

from .codes import CodeDropdown, AiiDACodeSetup
from .computers import SshComputerSetup
from .computers import AiidaComputerSetup
from .computers import ComputerDropdown
from .codes import AiiDACodeSetup, CodeDropdown
from .computers import AiidaComputerSetup, ComputerDropdown, SshComputerSetup
from .databases import (
CodQueryWidget,
CodeDatabaseWidget,
CodQueryWidget,
ComputerDatabaseWidget,
OptimadeQueryWidget,
)
Expand All @@ -28,24 +26,21 @@
RunningCalcJobOutputWidget,
SubmitButtonWidget,
)
from .structures import StructureManagerWidget
from .structures import (
BasicStructureEditor,
SmilesWidget,
StructureBrowserWidget,
StructureExamplesWidget,
StructureManagerWidget,
StructureUploadWidget,
SmilesWidget,
)
from .structures import BasicStructureEditor
from .viewers import viewer
from .viewers import register_viewer_widget

from .wizard import WizardAppWidget
from .wizard import WizardAppWidgetStep

from .viewers import AiidaNodeViewWidget, register_viewer_widget, viewer
from .wizard import WizardAppWidget, WizardAppWidgetStep

__all__ = [
"AiiDACodeSetup",
"AiidaComputerSetup",
"AiidaNodeViewWidget",
"BasicStructureEditor",
"CodQueryWidget",
"CodeDatabaseWidget",
Expand Down Expand Up @@ -79,4 +74,4 @@
"viewer",
]

__version__ = "1.0.0b18"
__version__ = "1.0.0b19"
181 changes: 181 additions & 0 deletions aiidalab_widgets_base/bug_report.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
"""Provide more user friendly error messages and automated reporting.
Authors:
* Carl Simon Adorf <simon.adorf@epfl.ch>
"""
import base64
import json
import platform
import re
import sys
import zlib
from textwrap import wrap
from urllib.parse import urlencode, urlsplit, urlunsplit

import ipywidgets as ipw
from aiidalab.utils import find_installed_packages
from ansi2html import Ansi2HTMLConverter


def get_environment_fingerprint(encoding="utf-8"):
packages = find_installed_packages()
data = {
"version": 1,
"platform": {
"architecture": platform.architecture(),
"python_version": platform.python_version(),
"version": platform.version(),
},
"packages": {package.name: package.version for package in packages},
}
json_data = json.dumps(data, separators=(",", ":"))
return base64.urlsafe_b64encode(zlib.compress(json_data.encode(encoding), level=9))


def parse_environment_fingerprint(data, encoding="utf-8"):
packages = json.loads(
zlib.decompress(base64.urlsafe_b64decode(data)).decode(encoding)
)
return packages


ERROR_MESSAGE = """<div class="alert alert-danger">
<p><strong>
<i class="fa fa-bug" aria-hidden="true"></i> Oh no... the application crashed due to an unexpected error.
</strong></p>
<a href="{issue_url}" target="_blank" class="btn btn-primary">
<i class="fa fa-share" aria-hidden="true"></i> Create bug report
</a>
<button
onclick="Jupyter.notebook.clear_all_output(); Jupyter.notebook.restart_run_all({{confirm: false}})"
type="button"
class="btn btn-success">
<i class="fa fa-refresh" aria-hidden="true"></i> Restart app
</button>
<div style="padding-top: 1em">
<details style="border: 1px solid #aaa; border-radius: 4px; padding: .5em .5em 0; ">
<summary style="font-weight: bold; margin: -.5em -.5em 0; padding: .5em">
<i class="fa fa-code" aria-hidden="true"></i> View the full traceback
</summary>
<pre style="color: #333; background: #f8f8f8;"><code>{traceback}</code></pre>
</details>
</div></div>"""


BUG_REPORT_TITLE = """Bug report: Application crashed with {exception_type}"""

BUG_REPORT_BODY = """## Automated report
_This issue was created with the app's automated bug reporting feature.
Attached to this issue is the full traceback as well as an environment
fingerprint that contains information about the operating system as well as all
installed libraries._
## Additional comments (optional):
_Example: I submitted a band structure calculation for Silica._
## Attachments
<details>
<summary>Traceback</summary>
```python-traceback
{traceback}
```
</details>
<details>
<summary>Environment fingerprint</summary>
<pre>{environment_fingerprint}</pre>
</details>
**By submitting this issue I confirm that I am aware that this information can
potentially be used to determine what kind of calculation was performed at the
time of error.**
"""


def _strip_ansi_codes(msg):
"""Remove any ANSI codes (e.g. color codes)."""
return re.sub(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])", "", msg)


def _convert_ansi_codes_to_html(msg):
"""Convert any ANSI codes (e.g. color codes) into HTML."""
converter = Ansi2HTMLConverter()
return converter.produce_headers().strip() + converter.convert(msg, full=False)


def _format_truncated_traceback(traceback, max_num_chars=3000):
"""Truncate the traceback to the given character length."""
n = 0
for i, line in enumerate(reversed(traceback)):
n += len(_strip_ansi_codes(line)) + 2 # add 2 for newline control characters
if n > max_num_chars:
break
return _strip_ansi_codes("\n".join(traceback[-i:]))


_ORIGINAL_EXCEPTION_HANDLER = None


def install_create_github_issue_exception_handler(output, url, labels=None):
"""Install a GitHub bug report exception handler.
After installing this handler, kernel exception will show a generic error
message to the user, with the option to file an automatic bug report at the
given URL.
"""
global _ORIGINAL_EXCEPTION_HANDLER

if labels is None:
labels = []

ipython = get_ipython() # noqa
_ORIGINAL_EXCEPTION_HANDLER = _ORIGINAL_EXCEPTION_HANDLER or ipython._showtraceback

def create_github_issue_exception_handler(exception_type, exception, traceback):
try:
output.clear_output()

bug_report_query = {
"title": BUG_REPORT_TITLE.format(
exception_type=str(exception_type.__name__)
),
"body": BUG_REPORT_BODY.format(
# Truncate the traceback to a maximum of 3000 characters
# and strip all ansi control characters:
traceback=_format_truncated_traceback(traceback, 3000),
# Determine and format the environment fingerprint to be
# included with the bug report:
environment_fingerprint="\n".join(
wrap(get_environment_fingerprint().decode("utf-8"), 100)
),
),
"labels": ",".join(labels),
}
issue_url = urlunsplit(
urlsplit(url)._replace(query=urlencode(bug_report_query))
)

with output:
msg = ipw.HTML(
ERROR_MESSAGE.format(
issue_url=issue_url,
traceback=_convert_ansi_codes_to_html("\n".join(traceback)),
len_url=len(issue_url),
)
)
display(msg) # noqa
except Exception as error:
print(f"Error while generating bug report: {error}", file=sys.stderr)
_ORIGINAL_EXCEPTION_HANDLER(exception_type, exception, traceback)

def restore_original_exception_handler():
ipython._showtraceback = _ORIGINAL_EXCEPTION_HANDLER

ipython._showtraceback = create_github_issue_exception_handler

return restore_original_exception_handler
6 changes: 3 additions & 3 deletions aiidalab_widgets_base/codes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
from subprocess import check_output

import ipywidgets as ipw
from aiida.orm import Code, Computer, QueryBuilder, User
from aiida.plugins.entry_point import get_entry_point_names
from IPython.display import clear_output
from traitlets import Bool, Dict, Instance, Unicode, Union, dlink, link, validate

from aiida.orm import Code, Computer, QueryBuilder, User
from aiida.plugins.entry_point import get_entry_point_names
from aiidalab_widgets_base.computers import ComputerDropdown


Expand Down Expand Up @@ -269,7 +269,7 @@ def _setup_code(self, _=None):

def exists(self):
"""Returns True if the code exists, returns False otherwise."""
from aiida.common import NotExistent, MultipleObjectsError
from aiida.common import MultipleObjectsError, NotExistent

try:
Code.get_from_string(f"{self.label}@{self.computer.name}")
Expand Down

0 comments on commit 6fa5f86

Please sign in to comment.