Merge branch 'master' into environment_fixes

This commit is contained in:
Bob Hyman 2020-09-17 03:05:14 -04:00
commit 9b2827ee80
22 changed files with 209 additions and 113 deletions

View file

@ -385,7 +385,7 @@
email: lalochcz@gmail.com
aliases:
- laloch
num_commits: 150
num_commits: 159
first_commit: 2018-07-26 13:51:50
github: laloch
- name: Nico Lehmann
@ -435,7 +435,7 @@
github: funkyfuture
- name: Anthony Scopatz
email: scopatz@gmail.com
num_commits: 2628
num_commits: 2635
first_commit: 2015-01-21 17:04:13
github: scopatz
- name: anatoly techtonik
@ -955,7 +955,7 @@
github: selepo
- name: Bob Hyman
email: bob.hyman@bobssoftwareworks.com
num_commits: 125
num_commits: 128
first_commit: 2016-06-26 16:48:47
github: bobhy
alternate_emails:
@ -1104,7 +1104,7 @@
email: anki-code@users.noreply.github.com
alternate_emails:
- anki-code
num_commits: 35
num_commits: 41
first_commit: 2019-10-15 18:20:58
github: xxh
- name: Sylvain Corlay
@ -1199,6 +1199,11 @@
github: mariusvniekerk
- name: Danny Sepler
email: dannysepler@gmail.com
num_commits: 2
num_commits: 10
first_commit: 2020-08-29 12:32:49
github: dannysepler
- name: Eadaen1
email: eadaen@protonmail.com
num_commits: 8
first_commit: 2020-09-11 04:05:11
github: Eadaen

View file

@ -30,12 +30,12 @@ Jean-Benoist Leger <jb@leger.tf> Jean-Benoist Leger <jbleger@hds.utc.fr>
christopher <cjwright4242@gmail.com>
Klaus Alexander Seistrup <klaus@seistrup.dk> Klaus Alexander Seistrup <kseistrup@users.noreply.github.com>
Leonardo Santagada <santagada@gmail.com>
anki-code <anki-code@users.noreply.github.com> anki-code <anki-code>
Burak Yiğit Kaya <ben@byk.im> Burak Yigit Kaya <ben@byk.im>
Matthias Bussonnier <bussonniermatthias@gmail.com>
Aaron Griffin <aig787@gmail.com>
Rob Brewer <rwb123@gmail.com> Robert W. Brewer <rwb123@gmail.com>
virus <virusbb001a@gmail.com>
anki-code <anki-code@users.noreply.github.com> anki-code <anki-code>
Sagar Tewari <sagartewari01@gmail.com> Sagar Tewari <sagartewariym@yahoo.com>
Konstantin Molchanov <kmolchanov@machinezone.com> Konstantin Molchanov <moigagoo@live.com>
Guillaume Leclerc <guillaume.leclerc@epfl.ch> Guillaume Leclerc <guillaume.leclerc.work@gmail.com>
@ -66,12 +66,14 @@ Daniel Shimon <daniel.shimon22@gmail.com>
Jason R. Coombs <jaraco@jaraco.com>
Gyuri Horak <dyuri@horak.hu>
Stephan Fitzpatrick <knowsuchagency@gmail.com>
Danny Sepler <dannysepler@gmail.com>
Will S <wsha.code@gmail.com>
K.-Michael Aye <kmichael.aye@gmail.com> K.-Michael Aye <michaelaye@users.noreply.github.com>
halloleo <git@halloleo.dot.hailmail.dot.net> halloleo <halloleo@users.noreply.github.com>
Will Wykeham <will@wykeham.net>
Oleh Prypin <oleh@pryp.in>
Brian Skinn <brian.skinn@gmail.com>
Eadaen1 <eadaen@protonmail.com>
Alessio Bogon <youtux@gmail.com> Alessio Bogon <youtux@users.noreply.github.com>
Yohei Tamura <tamuhey@gmail.com>
Maximilian Köhl <mail@koehlma.de>
@ -177,7 +179,6 @@ Chris Lasher <chris.lasher@gmail.com>
Edmund Miller <edmund.a.miller@protonmail.com>
Gabriel Vogel <gabriel.vogel@online.de>
anki <anki@code.email>
Danny Sepler <dannysepler@gmail.com>
Dan Allan <dallan@bnl.gov>
Ned Letcher <nletcher@gmail.com>
Zach Crownover <zachary.crownover@gmail.com>

View file

@ -15,12 +15,12 @@ Authors are sorted by number of commits.
* christopher
* Klaus Alexander Seistrup
* Leonardo Santagada
* anki-code
* Burak Yiğit Kaya
* Matthias Bussonnier
* Aaron Griffin
* Rob Brewer
* virus
* anki-code
* Sagar Tewari
* Konstantin Molchanov
* Guillaume Leclerc
@ -49,12 +49,14 @@ Authors are sorted by number of commits.
* Jason R. Coombs
* Gyuri Horak
* Stephan Fitzpatrick
* Danny Sepler
* Will S
* K.-Michael Aye
* halloleo
* Will Wykeham
* Oleh Prypin
* Brian Skinn
* Eadaen1
* Alessio Bogon
* Yohei Tamura
* Maximilian Köhl
@ -160,7 +162,6 @@ Authors are sorted by number of commits.
* Edmund Miller
* Gabriel Vogel
* anki
* Danny Sepler
* Dan Allan
* Ned Letcher
* Zach Crownover

View file

@ -4,6 +4,48 @@ Xonsh Change Log
.. current developments
v0.9.22
====================
**Added:**
* Added xontrib-argcomplete to support kislyuk/argcomplete - tab completion for argparse.
* New ``tools.debian_command_not_found()`` function for finding commands in
debian/ubuntu packages.
* New ``tools.conda_suggest_command_not_found()`` function for finding commands in
conda packages.
* Borrow shift-selection from prompt-toolkit. Shift-arrow (selects a letter) and control-shift-arrow (selects a word) should now be supported.
* Documentation for keyboard shortcuts
* Xonsh now supports bash-style variable assignments preceding
subprocess commands (e.g. ``$FOO="bar" bash -c r"echo $FOO"``).
**Changed:**
* Added the fastest way to run xonsh AppImage to the docs.
* ``command_not_found()`` is now a wrapper function that finds packages for missing
commands in a variety of locations. This function now also takes an ``env`` argument
for looking up values in the enviornment.
* The variable cwd_dir, used for prompts,
now always has a slash at the end, so users can use the
construct "{cwd_dir}{cwd_base}" in their custom prompt definitions.
**Fixed:**
* crash when starting wizard by ``xonfig wizard``
xonsh.environ: ensure get_docs(name).doc_default is str when name is not registered.
* Fixed issue where xontribs were failing from ``AttributeError: '_MergedKeyBindings' object has no attribute 'add'``
**Authors:**
* Anthony Scopatz
* David Strobach
* Bob Hyman
* anki-code
* Danny Sepler
* Eadaen1
v0.9.21
====================

View file

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application">
<id>xonsh</id>
<metadata_license>MIT</metadata_license>
<project_license>Python-2.0</project_license>
<metadata_license>BSD</metadata_license>
<project_license>BSD</project_license>
<name>Xonsh</name>
<summary>Xonsh on Python {{ python-fullversion }}</summary>
<description>

View file

@ -3,14 +3,40 @@ Via AppImage
`AppImage <https://appimage.org/>`_ is a format for distributing portable software on Linux without needing superuser permissions to install the application. It tries also to allow Linux distribution-agnostic binary software deployment for application developers, also called Upstream packaging.
AppImage allows xonsh to be run on any AppImage supported Linux distributive without installation and root access.
In short the AppImage is one executable file which contains both xonsh and Python. AppImage allows xonsh to be run on any AppImage supported Linux distribution without installation or root access.
Get AppImage from Github
------------------------
You can get the xonsh AppImage from GitHub and run it on your Linux machine without installing it:
.. code-block:: bash
wget https://github.com/xonsh/xonsh/releases/latest/download/xonsh-x86_64.AppImage -O xonsh
chmod +x xonsh
./xonsh
If you don't have Python on your host, you may want to get it from AppImage:
.. code-block:: xonsh
./xonsh
$PATH = [f'{$APPDIR}/usr/bin'] + $PATH
python -m pip install tqdm --user # the package will be installed to ~/.local/
import tqdm
Building your own xonsh AppImage
--------------------------------
The best way to build xonsh AppImage in 5 minutes is to using `python-appimage <https://github.com/niess/python-appimage>`_:
.. code-block:: bash
mkdir -p /tmp/test && cd /tmp/test
git clone https://github.com/niess/python-appimage
cd python-appimage
python -m python_appimage build app applications/xonsh
mkdir -p /tmp/build && cd /tmp/build
git clone --depth 1 https://github.com/xonsh/xonsh
cd xonsh/appimage
echo 'xonsh' > requirements.txt
cat pre-requirements.txt >> requirements.txt # here you can add your additional PyPi packages to pack them into AppImage
cd ..
pip install git+https://github.com/niess/python-appimage
python -m python_appimage build app ./appimage
./xonsh-x86_64.AppImage

View file

@ -19,3 +19,4 @@ Guides
bash_to_xsh
subproc_types
python_virtual_environments
keyboard_shortcuts

View file

@ -0,0 +1,34 @@
.. _keyboard_shortcuts:
******************
Keyboard Shortcuts
******************
Xonsh comes pre-baked with a few keyboard shortcuts. The following is only available under the prompt-toolkit shell.
.. list-table::
:widths: 40 60
:header-rows: 1
* - Shortcut
- Description
* - Ctrl-X + Ctrl-E
- Open the current buffer in your default text editor.
* - Ctrl-D
- Exit xonsh and return to original terminal. If not called by another terminal, then exit current terminal window. Similar to ``exit``.
* - Ctrl-J
- | Similar to enter in a few respects:
| 1. Execute the current buffer.
| 2. End and execute a multiline statement.
* - Ctrl-M
- Same as Ctrl-J
* - Shift-Left OR Shift-Right *[Beta]*
- Highlight one character in either direction
* - Ctrl-Shift-Left OR Ctrl-Shift-Right *[Beta]*
- Highlight one word in either direction
* - Ctrl-X + Ctrl-C *[Beta]*
- Copy highlighted section
* - Ctrl-X + Ctrl-X *[Beta]*
- Cut highlighted section
* - Ctrl-V *[Beta]*
- Paste clipboard contents

View file

@ -1447,7 +1447,7 @@ By default, the following variables are available for use:
set the character used in shortened path.
* ``short_cwd``: A shortened form of the current working directory; e.g.,
``/path/to/xonsh`` becomes ``/p/t/xonsh``
* ``cwd_dir``: The dirname of the current working directory, e.g. ``/path/to`` in
* ``cwd_dir``: The dirname of the current working directory, e.g. ``/path/to/`` in
``/path/to/xonsh``.
* ``cwd_base``: The basename of the current working directory, e.g. ``xonsh`` in
``/path/to/xonsh``.

View file

@ -1,6 +1,6 @@
**Added:**
* Added xontrib-argcomplete to support kislyuk/argcomplete - tab completion for argparse.
* <news item>
**Changed:**
@ -16,7 +16,7 @@
**Fixed:**
* <news item>
* cygwin needs full path to find exe; disable thread_subprocs as default for cygwin
**Security:**

View file

@ -1,23 +0,0 @@
**Added:**
* Borrow shift-selection from prompt-toolkit. Shift-arrow (selects a letter) and control-shift-arrow (selects a word) should now be supported.
**Changed:**
* <news item>
**Deprecated:**
* <news item>
**Removed:**
* <news item>
**Fixed:**
* <news item>
**Security:**
* <news item>

View file

@ -1,24 +0,0 @@
**Added:**
* Xonsh now supports bash-style variable assignments preceding
subprocess commands (e.g. ``$FOO = "bar" bash -c r"echo $FOO"``).
**Changed:**
* <news item>
**Deprecated:**
* <news item>
**Removed:**
* <news item>
**Fixed:**
* <news item>
**Security:**
* <news item>

View file

@ -2240,6 +2240,10 @@ def test_bang_ls_envvar_listval():
check_xonsh_ast({"WAKKA": [".", "."]}, "!(ls $WAKKA)", False)
def test_bang_envvar_args():
check_xonsh_ast({"LS": "ls"}, "!($LS .)", False)
def test_question():
check_xonsh_ast({}, "range?")
@ -2502,7 +2506,7 @@ def test_ls_quotes_3_space():
def test_leading_envvar_assignment():
check_xonsh_ast({}, "![$FOO= 'foo' $BAR =2 echo r'$BAR']", False)
check_xonsh_ast({}, "![$FOO='foo' $BAR=2 echo r'$BAR']", False)
def test_echo_comma():

View file

@ -11,7 +11,7 @@ parser = argparse.ArgumentParser(description=program_description)
parser.add_argument("env", nargs="*", default=[], metavar="ENV=value")
parser.add_argument("--python", "-p", default="3.6", metavar="python_version")
parser.add_argument("--pypy", default=None, metavar="pypy_version")
parser.add_argument("--ptk", "-t", default="2.0.4", metavar="ptk_version")
parser.add_argument("--ptk", "-t", default="3.0.7", metavar="ptk_version")
parser.add_argument("--keep", action="store_true")
parser.add_argument("--build", action="store_true")
parser.add_argument("--command", "-c", default="xonsh", metavar="command")
@ -30,7 +30,9 @@ WORKDIR /xonsh
ADD ./ ./
RUN python setup.py install
""".format(
python_version=args.python, ptk_version=args.ptk, pytest="pytest" if args.pytest else ""
python_version=args.python,
ptk_version=args.ptk,
pytest="pytest" if args.pytest else "",
)
if args.pypy:
@ -46,7 +48,9 @@ WORKDIR /xonsh
ADD ./ ./
RUN pypy3 setup.py install
""".format(
python_version=args.pypy, ptk_version=args.ptk, pytest="pytest" if args.pytest else ""
python_version=args.pypy,
ptk_version=args.ptk,
pytest="pytest" if args.pytest else "",
)
print("Building and running Xonsh")

View file

@ -1,4 +1,4 @@
__version__ = "0.9.21"
__version__ = "0.9.22"
# amalgamate exclude jupyter_kernel parser_table parser_test_table pyghooks

View file

@ -27,7 +27,7 @@ from xonsh.inspectors import Inspector
from xonsh.aliases import Aliases, make_default_aliases
from xonsh.environ import Env, default_env, locate_binary
from xonsh.jobs import add_job
from xonsh.platform import ON_POSIX, ON_WINDOWS, ON_WSL
from xonsh.platform import ON_POSIX, ON_WINDOWS, ON_WSL, ON_CYGWIN
from xonsh.proc import (
PopenThread,
ProcProxyThread,
@ -212,7 +212,13 @@ def get_script_subproc_command(fname, args):
"""
# make sure file is executable
if not os.access(fname, os.X_OK):
raise PermissionError
if not ON_CYGWIN:
raise PermissionError
# explicitly look at all PATH entries for cmd
w_path = os.getenv("PATH").split(":")
w_fpath = list(map(lambda p: p + os.sep + fname, w_path))
if not any(list(map(lambda c: os.access(c, os.X_OK), w_fpath))):
raise PermissionError
if ON_POSIX and not os.access(fname, os.R_OK):
# on some systems, some important programs (e.g. sudo) will have
# execute permissions but not read/write permissions. This enables
@ -1365,9 +1371,7 @@ def xonsh_builtins(execer=None):
class XonshSession:
"""All components defining a xonsh session.
"""
"""All components defining a xonsh session."""
def __init__(self, execer=None, ctx=None):
"""

View file

@ -25,6 +25,7 @@ from xonsh.platform import (
PATH_DEFAULT,
ON_WINDOWS,
ON_LINUX,
ON_CYGWIN,
os_environ,
)
@ -1307,7 +1308,7 @@ def DEFAULT_VARS():
is_bool_or_none,
to_bool_or_none,
bool_or_none_to_str,
True,
True if not ON_CYGWIN else False,
"Whether or not to try to run subrocess mode in a Python thread, "
"when applicable. There are various trade-offs, which normally "
"affects only interactive sessions.\n\nWhen True:\n\n"
@ -1869,11 +1870,9 @@ class Env(cabc.MutableMapping):
def get_docs(self, key, default=None):
"""Gets the documentation for the environment variable."""
vd = self._vars.get(key, None)
vd = self._vars.get(key, default)
if vd is None:
if default is None:
default = Var()
return default
vd = Var(default="", doc_default="")
if vd.doc_default is DefaultNotGiven:
var_default = self._vars.get(key, "<default not set>").default
dval = (

View file

@ -3325,11 +3325,7 @@ class BaseParser(object):
p[0] = ast.Str(s=p1.value, lineno=p1.lineno, col_offset=p1.lexpos)
def p_envvar_assign_left(self, p):
"""envvar_assign_left : dollar_name_tok EQUALS
| dollar_name_tok WS EQUALS
| dollar_name_tok EQUALS WS
| dollar_name_tok WS EQUALS WS
"""
"""envvar_assign_left : dollar_name_tok EQUALS"""
p[0] = p[1]
def p_envvar_assign(self, p):

View file

@ -95,7 +95,7 @@ def PROMPT_FIELDS():
prompt_end="#" if xt.is_superuser() else "$",
hostname=socket.gethostname().split(".", 1)[0],
cwd=_dynamically_collapsed_pwd,
cwd_dir=lambda: os.path.dirname(_replace_home_cwd()),
cwd_dir=lambda: os.path.join(os.path.dirname(_replace_home_cwd()), ""),
cwd_base=lambda: os.path.basename(_replace_home_cwd()),
short_cwd=_collapsed_pwd,
curr_branch=current_branch,

View file

@ -94,12 +94,7 @@ class PromptToolkitShell(BaseShell):
self.history = ThreadedHistory(PromptToolkitHistory())
self.prompter = PromptSession(history=self.history)
self.pt_completer = PromptToolkitCompleter(self.completer, self.ctx, self)
self.key_bindings = merge_key_bindings(
[
load_xonsh_bindings(),
load_emacs_shift_selection_bindings(),
]
)
self.key_bindings = load_xonsh_bindings()
# Store original `_history_matches` in case we need to restore it
self._history_matches_orig = self.prompter.default_buffer._history_matches
@ -110,6 +105,11 @@ class PromptToolkitShell(BaseShell):
completer=self.pt_completer,
bindings=self.key_bindings,
)
# Goes at the end, since _MergedKeyBindings objects do not have
# an add() function, which is necessary for on_ptk_create events
self.key_bindings = merge_key_bindings(
[self.key_bindings, load_emacs_shift_selection_bindings()]
)
def singleline(
self, auto_suggest=None, enable_history_search=True, multiline=True, **kwargs

View file

@ -515,9 +515,9 @@ def _have_open_triple_quotes(s):
def get_line_continuation():
""" The line continuation characters used in subproc mode. In interactive
mode on Windows the backslash must be preceded by a space. This is because
paths on Windows may end in a backslash.
"""The line continuation characters used in subproc mode. In interactive
mode on Windows the backslash must be preceded by a space. This is because
paths on Windows may end in a backslash.
"""
if (
ON_WINDOWS
@ -691,7 +691,7 @@ def indent(instr, nspaces=4, ntabs=0, flatten=False):
def get_sep():
""" Returns the appropriate filepath separator char depending on OS and
"""Returns the appropriate filepath separator char depending on OS and
xonsh options set
"""
if ON_WINDOWS and builtins.__xonsh__.env.get("FORCE_POSIX_PATHS"):
@ -814,7 +814,7 @@ def executables_in(path):
return
def command_not_found(cmd):
def debian_command_not_found(cmd):
"""Uses the debian/ubuntu command-not-found utility to suggest packages for a
command that cannot currently be found.
"""
@ -839,6 +839,33 @@ def command_not_found(cmd):
return s
def conda_suggest_command_not_found(cmd, env):
"""Uses conda-suggest to suggest packages for a command that cannot
currently be found.
"""
try:
from conda_suggest import find
except ImportError:
return ""
return find.message_string(
cmd, conda_suggest_path=env.get("CONDA_SUGGEST_PATH", None)
)
def command_not_found(cmd, env):
"""Uses various mechanism to suggest packages for a command that cannot
currently be found.
"""
if ON_LINUX:
rtn = debian_command_not_found(cmd)
else:
rtn = ""
conda = conda_suggest_command_not_found(cmd, env)
if conda:
rtn = rtn + "\n\n" + conda if rtn else conda
return rtn
def suggest_commands(cmd, env, aliases):
"""Suggests alternative commands given an environment and aliases."""
if not env.get("SUGGEST_COMMANDS"):
@ -871,7 +898,7 @@ def suggest_commands(cmd, env, aliases):
num = min(len(suggested), max_sugg)
if num == 0:
rtn = command_not_found(cmd)
rtn = command_not_found(cmd, env)
else:
oneof = "" if num == 1 else "one of "
tips = "Did you mean {}the following?".format(oneof)
@ -881,7 +908,7 @@ def suggest_commands(cmd, env, aliases):
" {: <{}} {}".format(key + ":", length, val) for key, val in items
)
rtn = "{}\n{}".format(tips, alternatives)
c = command_not_found(cmd)
c = command_not_found(cmd, env)
rtn += ("\n\n" + c) if len(c) > 0 else ""
return rtn
@ -1011,7 +1038,7 @@ def escape_windows_cmd_string(s):
def argvquote(arg, force=False):
""" Returns an argument quoted in such a way that that CommandLineToArgvW
"""Returns an argument quoted in such a way that that CommandLineToArgvW
on Windows will return the argument string unchanged.
This is the same thing Popen does when supplied with an list of arguments.
Arguments in a command line should be separated by spaces; this
@ -1481,8 +1508,8 @@ def bool_seq_to_csv(x):
def ptk2_color_depth_setter(x):
""" Setter function for $PROMPT_TOOLKIT_COLOR_DEPTH. Also
updates os.environ so prompt toolkit can pickup the value.
"""Setter function for $PROMPT_TOOLKIT_COLOR_DEPTH. Also
updates os.environ so prompt toolkit can pickup the value.
"""
x = str(x)
if x in {
@ -1660,7 +1687,7 @@ def is_history_backend(x):
def is_dynamic_cwd_width(x):
""" Determine if the input is a valid input for the DYNAMIC_CWD_WIDTH
"""Determine if the input is a valid input for the DYNAMIC_CWD_WIDTH
environment variable.
"""
return (
@ -1872,7 +1899,7 @@ WIN_BOLD_COLOR_MAP = LazyObject(_win_bold_color_map, globals(), "WIN_BOLD_COLOR_
def hardcode_colors_for_win10(style_map):
"""Replace all ansi colors with hardcoded colors to avoid unreadable defaults
in conhost.exe
in conhost.exe
"""
modified_style = {}
if not builtins.__xonsh__.env["PROMPT_TOOLKIT_COLOR_DEPTH"]:
@ -1898,8 +1925,7 @@ def hardcode_colors_for_win10(style_map):
def ansicolors_to_ptk1_names(stylemap):
"""Converts ansicolor names in a stylemap to old PTK1 color names
"""
"""Converts ansicolor names in a stylemap to old PTK1 color names"""
if pygments_version_info() and pygments_version_info() >= (2, 4, 0):
return stylemap
modified_stylemap = {}
@ -1913,7 +1939,7 @@ def ansicolors_to_ptk1_names(stylemap):
def intensify_colors_for_cmd_exe(style_map):
"""Returns a modified style to where colors that maps to dark
colors are replaced with brighter versions.
colors are replaced with brighter versions.
"""
modified_style = {}
replace_colors = {

View file

@ -296,9 +296,9 @@ def make_envvar(name):
"""Makes a StoreNonEmpty node for an environment variable."""
env = builtins.__xonsh__.env
vd = env.get_docs(name)
if not vd.configurable:
if not vd.doc_configurable:
return
default = vd.default
default = vd.doc_default
if "\n" in default:
default = "\n" + _wrap_paragraphs(default, width=69)
curr = env.get(name)
@ -311,7 +311,7 @@ def make_envvar(name):
name=name,
default=default,
current=curr,
docstr=_wrap_paragraphs(vd.docstr, width=69),
docstr=_wrap_paragraphs(vd.doc, width=69),
)
mnode = wiz.Message(message=msg)
converter = env.get_converter(name)
@ -322,7 +322,7 @@ def make_envvar(name):
show_conversion=True,
path=path,
retry=True,
store_raw=vd.store_as_str,
store_raw=vd.doc_store_as_str,
)
return mnode, pnode
@ -340,7 +340,7 @@ def _make_flat_wiz(kidfunc, *args):
def make_env_wiz():
"""Makes an environment variable wizard."""
w = _make_flat_wiz(make_envvar, sorted(builtins.__xonsh__.env._docs.keys()))
w = _make_flat_wiz(make_envvar, sorted(builtins.__xonsh__.env.keys()))
return w