Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

hotfix/PyWry Linux Wheels-Import Error Handing #4561

Merged
merged 16 commits into from
Mar 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 4 additions & 68 deletions .github/workflows/unit-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,26 +82,10 @@ jobs:

- name: Setup sudo apt installs for ubuntu-latest
run: |
curl --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable
sudo apt-get update
sudo apt-get install -y \
build-essential \
libgtk-3-dev \
libsoup-3.0-dev \
libsoup2.4-dev \
libssl-dev \
curl \
wget \
squashfs-tools \
gcc \
g++ \
make \
file \
librsvg2-dev \
libwebkit2gtk-4.1-dev \
javascriptcoregtk-4.0 \
libwebkit2gtk-4.0-dev \
libayatana-appindicator3-dev
libwebkit2gtk-4.0-dev

- name: Load cached venv
id: cached-poetry-dependencies
Expand Down Expand Up @@ -160,26 +144,10 @@ jobs:

- name: Setup sudo apt installs for ubuntu-latest
run: |
curl --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable
sudo apt-get update
sudo apt-get install -y \
build-essential \
libgtk-3-dev \
libsoup-3.0-dev \
libsoup2.4-dev \
libssl-dev \
curl \
wget \
squashfs-tools \
gcc \
g++ \
make \
file \
librsvg2-dev \
libwebkit2gtk-4.1-dev \
javascriptcoregtk-4.0 \
libwebkit2gtk-4.0-dev \
libayatana-appindicator3-dev
libwebkit2gtk-4.0-dev

- name: Load cached venv
id: cached-poetry-dependencies
Expand Down Expand Up @@ -243,26 +211,10 @@ jobs:
- name: Setup sudo apt installs for ubuntu-latest
if: runner.os == 'Linux'
run: |
curl --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable
sudo apt-get update
sudo apt-get install -y \
build-essential \
libgtk-3-dev \
libsoup-3.0-dev \
libsoup2.4-dev \
libssl-dev \
curl \
wget \
squashfs-tools \
gcc \
g++ \
make \
file \
librsvg2-dev \
libwebkit2gtk-4.1-dev \
javascriptcoregtk-4.0 \
libwebkit2gtk-4.0-dev \
libayatana-appindicator3-dev
libwebkit2gtk-4.0-dev

- name: Load cached venv
id: cached-poetry-dependencies
Expand Down Expand Up @@ -324,26 +276,10 @@ jobs:
- name: Setup sudo apt installs for ubuntu-latest
if: runner.os == 'Linux'
run: |
curl --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable
sudo apt-get update
sudo apt-get install -y \
build-essential \
libgtk-3-dev \
libsoup-3.0-dev \
libsoup2.4-dev \
libssl-dev \
curl \
wget \
squashfs-tools \
gcc \
g++ \
make \
file \
librsvg2-dev \
libwebkit2gtk-4.1-dev \
javascriptcoregtk-4.0 \
libwebkit2gtk-4.0-dev \
libayatana-appindicator3-dev
libwebkit2gtk-4.0-dev

- name: Checkout
uses: actions/checkout@v3
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ logs/
.DS_Store
*.env
.venv
venv*/
venv
.vscode
*.ipynb
Expand Down
6 changes: 0 additions & 6 deletions build/docker/openbb.dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ RUN apt-get -y install --no-install-recommends \
libwebkit2gtk-4.0-dev \
build-essential \
libssl-dev \
libgtk-3-dev \
libayatana-appindicator3-dev \
librsvg2-dev \
ffmpeg \
python3-tk
Expand All @@ -44,12 +42,8 @@ WORKDIR /home/python
# SETUP POETRY IMAGE
FROM debian as poetry

RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain nightly

ENV PATH="/home/python/.cargo/bin:${PATH}"
ENV PATH="/home/python/.local/bin:${PATH}"

RUN /bin/bash -c "source $HOME/.cargo/env"
RUN pip install --upgrade pip wheel
RUN pip install poetry==1.3.2

Expand Down
8 changes: 6 additions & 2 deletions openbb_terminal/core/models/preferences_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,16 @@ class PreferencesModel(BaseModel):
# See more: https://matplotlib.org/stable/tutorials/introductory/usage.html#the-builtin-backends
PLOT_BACKEND: Optional[str] = None
PLOT_DPI: PositiveInt = 100
PLOT_HEIGHT: PositiveInt = 762
PLOT_WIDTH: PositiveInt = 1400
PLOT_HEIGHT: PositiveInt = 500
PLOT_WIDTH: PositiveInt = 800
PLOT_HEIGHT_PERCENTAGE: PositiveFloat = 50.0
PLOT_WIDTH_PERCENTAGE: PositiveFloat = 70.0
# Whether to open plot image exports after they are created
PLOT_OPEN_EXPORT: bool = False
# Use interactive window to display plots
PLOT_ENABLE_PYWRY: bool = True
PLOT_PYWRY_WIDTH: PositiveInt = 1400
PLOT_PYWRY_HEIGHT: PositiveInt = 762

# FEATURE FLAGS
SYNC_ENABLED: bool = True
Expand Down
24 changes: 18 additions & 6 deletions openbb_terminal/core/plots/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,24 @@
import aiohttp
import pandas as pd
import plotly.graph_objects as go
import pywry
from packaging import version
from reportlab.graphics import renderPDF

try:
from pywry.core import PyWry

PYWRY_AVAILABLE = True
except ImportError:
PYWRY_AVAILABLE = False

from svglib.svglib import svg2rlg

from openbb_terminal.base_helpers import console, strtobool
from openbb_terminal.core.session.current_user import get_current_user

if not PYWRY_AVAILABLE:
from openbb_terminal.core.plots.no_import import DummyBackend as PyWry # noqa

try:
from IPython import get_ipython

Expand All @@ -41,7 +51,7 @@
BACKEND = None


class Backend(pywry.PyWry):
class Backend(PyWry):
"""Custom backend for Plotly."""

def __new__(cls, *args, **kwargs): # pylint: disable=W0613
Expand All @@ -62,6 +72,8 @@ def __init__(self, daemon: bool = True, max_retries: int = 30):
and not strtobool(os.environ.get("OPENBB_ENABLE_QUICK_EXIT", False))
and current_process().name == "MainProcess"
)
if PyWry.__version__ == "0.0.0":
self.isatty = False

self.WIDTH, self.HEIGHT = 1400, 762

Expand All @@ -70,8 +82,8 @@ def __init__(self, daemon: bool = True, max_retries: int = 30):
def set_window_dimensions(self):
"""Set the window dimensions."""
current_user = get_current_user()
width = current_user.preferences.PLOT_WIDTH or 1400
height = current_user.preferences.PLOT_HEIGHT or 762
width = current_user.preferences.PLOT_PYWRY_WIDTH or 1400
height = current_user.preferences.PLOT_PYWRY_HEIGHT or 762

self.WIDTH, self.HEIGHT = int(width), int(height)

Expand Down Expand Up @@ -333,8 +345,8 @@ def start(self, debug: bool = False):
async def check_backend(self):
"""Override to check if isatty."""
if self.isatty:
if not hasattr(pywry, "__version__") or version.parse(
pywry.__version__
if not hasattr(PyWry, "__version__") or version.parse(
PyWry.__version__
) < version.parse("0.3.5"):
console.print(
"[bold red]Pywry version 0.3.5 or higher is required to use the "
Expand Down
85 changes: 85 additions & 0 deletions openbb_terminal/core/plots/no_import.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import asyncio
from typing import List

import dotenv

from openbb_terminal.base_helpers import console
from openbb_terminal.core.config.paths import SETTINGS_ENV_FILE
from openbb_terminal.core.session.current_user import get_current_user

__version__ = "0.0.0"

pywry_missing = """
[red]PyWry is not installed or missing required linux dependencies.[/]

[yellow]Install PyWry[/]
[green]pip install pywry --upgrade[/]

[yellow]Platform-specific notes[/]
Here is the underlying web engine each platform uses you might need to install.

[green]Linux[/]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice

Pywry uses gtk-rs and its related libraries for window creation and Wry also needs WebKitGTK for WebView.
To activate interactive plots/tables in pywry window, please make sure the following packages are installed:

[yellow]Arch Linux / Manjaro:[/]
[green]sudo pacman -S webkit2gtk-4.0[/]\n
[yellow]Debian / Ubuntu:[/]
[green]sudo apt install libwebkit2gtk-4.0-dev[/]\n
[yellow]Fedora / CentOS / AlmaLinux:[/]
[green]sudo dnf install gtk3-devel webkit2gtk4.0-devel[/]\r
"""


class DummyBackend:
"""Dummy class to avoid import errors."""

max_retries = 0
outgoing: List[str] = []
init_engine: List[str] = []
daemon = True
debug = False
shell = False
base = None

def __new__(cls, *args, **kwargs): # pylint: disable=W0613
"""Create a singleton instance of the backend."""
if not hasattr(cls, "instance"):
cls.instance = super().__new__(cls) # pylint: disable=E1120
return cls.instance

def __init__(self, daemon: bool = True, max_retries: int = 30):
"""Dummy init to avoid import errors."""
self.daemon = daemon
self.max_retries = max_retries
try:
self.loop: asyncio.AbstractEventLoop = asyncio.get_event_loop()
except RuntimeError:
self.loop = asyncio.new_event_loop()
asyncio.set_event_loop(self.loop)

current_user = get_current_user()

# If pywry is not installed or missing required linux dependencies
# we inform the user the required packages to install and revert
# plotly default behaviour to open in browser.
# We do this only once.
if current_user.preferences.PLOT_ENABLE_PYWRY:
console.print(pywry_missing)
if console.input(
"If you prefer to continue without interactive plots/tables, "
"press [green]enter[/] or [red]ctrl+c[/] to exit."
):
dotenv.set_key(SETTINGS_ENV_FILE, "PLOT_ENABLE_PYWRY", "0")

current_user.preferences.USE_INTERACTIVE_DF = False

def close(self, reset: bool = False): # pylint: disable=W0613
pass

def start(self, debug: bool = False): # pylint: disable=W0613
pass

async def check_backend(self):
"""Dummy check backend method to avoid errors and revert to browser."""
raise Exception
9 changes: 5 additions & 4 deletions openbb_terminal/core/plots/plotly_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ def __init__(self, fig: Optional[go.Figure] = None, **kwargs) -> None:
self._feature_flags_applied = False
self._exported = False
self._cmd_xshift = 0
self._bar_width = 0.0001
self._bar_width = 0.2
self._subplot_xdates: Dict[int, Dict[int, List[Any]]] = {}

if xaxis := kwargs.pop("xaxis", None):
Expand Down Expand Up @@ -806,7 +806,7 @@ def horizontal_legend(

@staticmethod
def chart_volume_scaling(
df_volume: pd.DataFrame, range_x: int = 4
df_volume: pd.DataFrame, range_x: int = 7
) -> Dict[str, list]:
"""Takes df_volume and returns volume_ticks, tickvals for chart volume scaling

Expand All @@ -815,7 +815,7 @@ def chart_volume_scaling(
df_volume : pd.DataFrame
Dataframe of volume (e.g. df_volume = df["Volume"])
range_x : int, optional
Number to multiply volume, by default 4
Number to multiply volume, by default 7

Returns
-------
Expand Down Expand Up @@ -879,9 +879,10 @@ def add_inchart_volume(
yaxis="y2",
row=row,
col=col,
opacity=0.5,
secondary_y=False,
)
ticksize = 14 - (self.subplots_kwargs["rows"] // 1.5)
ticksize = 14 - (self.subplots_kwargs["rows"] // 2)
self.update_layout(
yaxis=dict(
fixedrange=True,
Expand Down
5 changes: 5 additions & 0 deletions openbb_terminal/core/plots/plotly_ta/ta_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,13 +347,18 @@ def init_plot(self, symbol: str = "", candles: bool = True) -> OpenBBFigure:
row_width=[1],
specs=[[{"secondary_y": True}]],
)
cc_linewidth = (
0.8 if len(self.df_stock.index) > 500 else 0.9 if self.intraday else 1.1
)
if candles:
fig.add_candlestick(
x=self.df_stock.index,
open=self.df_stock.Open,
high=self.df_stock.High,
low=self.df_stock.Low,
close=self.df_stock.Close,
decreasing=dict(line=dict(width=cc_linewidth)),
increasing=dict(line=dict(width=cc_linewidth)),
name=f"{symbol} OHLC",
showlegend=False,
row=1,
Expand Down
5 changes: 4 additions & 1 deletion openbb_terminal/core/plots/web/bar_menus.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ function autoScaling(eventdata, graphs) {
};

yaxis_unique.forEach((unique) => {
if (typeof unique != "string") {
return;
}
let yaxis = "yaxis" + unique.replace("y", "");
let y_candle = [];
let y_values = [];
Expand Down Expand Up @@ -92,7 +95,7 @@ function autoScaling(eventdata, graphs) {

if (is_volume) {
if (graphs.layout[yaxis].tickvals != undefined) {
const range_x = 4;
const range_x = 7;
let volume_ticks = org_y_max;
let round_digits = -3;
let first_val = Math.round(volume_ticks * 0.2, round_digits);
Expand Down
Loading