Skip to content

Commit

Permalink
Add settings configuration dependent of Python executable. Add --sett…
Browse files Browse the repository at this point in the history
…ingspath option (#2760)
  • Loading branch information
HelioGuilherme66 authored May 6, 2024
1 parent 1b2d508 commit d6e2f2e
Show file tree
Hide file tree
Showing 11 changed files with 123 additions and 40 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ and this project adheres to http://semver.org/spec/v2.0.0.html[Semantic Versioni

=== Added

- Added command line option, ``--settingspath``, to select a different configuration. For example, you can have different
colour settings or UI language.
- Added different settings file, according the actual Python executable, if not the original installed.
This allows different configurations, like, for example, in a virtual environment
- Added context option ``Open Containing Folder`` to test suites directories in Project Explorer.
- Added a setting for a specific file manager by editing the settings.cfg file. Add the string parameter ``file manager`` in the section ``[General]``.
- Added minimal support to have comment lines in Import settings. These are not supposed to be edited in Editor, and new lines are added at Text Editor.
Expand Down
51 changes: 32 additions & 19 deletions src/robotide/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

"""RIDE -- Robot Framework test data editor
Usage: ride.py [--noupdatecheck] [--debugconsole] [--version] [inpath]
Usage: ride.py [--noupdatecheck] [--debugconsole] [--settingspath <full path|settings filename>] [--version] [inpath]
RIDE can be started either without any arguments or by giving a path to a test
data file or directory to be opened.
Expand All @@ -24,6 +24,8 @@
To start debug console for RIDE problem debugging use --debugconsole option.
To use different settings use the option --settingspath followed by the path to the settings file or file name.
To see RIDE's version use --version.
RIDE's API is still evolving while the project is moving towards the 1.0
Expand All @@ -35,8 +37,8 @@
from string import Template

errorMessageTemplate = Template("""$reason
RIDE depends on wx (wxPython). Known versions for Python3 are: 4.0.7.post2, 4.1.1 and 4.2.0.\
At the time of this release the current wxPython version is 4.2.0.\
RIDE depends on wx (wxPython). Known versions for Python3 are: 4.0.7.post2, 4.1.1 and 4.2.1.\
At the time of this release the current wxPython version is 4.2.1.\
You can install with 'pip install wxPython' on most operating systems, or find the \
the download link from https://wxPython.org/""")

Expand All @@ -55,10 +57,6 @@

def main(*args):
_replace_std_for_win()
noupdatecheck, debug_console, inpath = _parse_args(args)
if len(args) > 3 or '--help' in args:
print(__doc__)
sys.exit()
if '--version' in args:
try:
from . import version
Expand All @@ -67,8 +65,12 @@ def main(*args):
sys.exit(1)
print(version.VERSION)
sys.exit(0)
noupdatecheck, debug_console, settings_path, inpath = _parse_args(args)
if len(args) > 3 or '--help' in args:
print(__doc__)
sys.exit()
try:
_run(inpath, not noupdatecheck, debug_console)
_run(inpath, not noupdatecheck, debug_console, settingspath=settings_path)
except Exception: # DEBUG
import traceback
traceback.print_exception(*sys.exc_info())
Expand All @@ -77,23 +79,34 @@ def main(*args):

def _parse_args(args):
if not args:
return False, False, None
noupdatecheck = '--noupdatecheck' in args
debug_console = '--debugconsole' in args
inpath = args[-1] if args[-1] not in ['--noupdatecheck',
'--debugconsole'] else None
return noupdatecheck, debug_console, inpath


def _run(inpath=None, updatecheck=True, debug_console=False):
return False, False, None, None
arguments = list(args)
noupdatecheck = '--noupdatecheck' in arguments
if noupdatecheck:
arguments.remove('--noupdatecheck')
debug_console = '--debugconsole' in arguments
if debug_console:
arguments.remove('--debugconsole')
settings_path = None
if '--settingspath' in arguments:
arguments.remove('--settingspath')
if len(arguments) > 0:
settings_path = arguments.pop(0)
else:
settings_path = None
inpath = arguments[0] if arguments else None
return noupdatecheck, debug_console, settings_path, inpath


def _run(inpath=None, updatecheck=True, debug_console=False, settingspath=None):
# print(f"DEBUG: ENTER _run {inpath=}, {updatecheck=}, {debug_console=}")
try:
from robotide.application import RIDE
from robotide.application import debugconsole
except ImportError:
_show_old_wxpython_warning_if_needed()
raise
ride = RIDE(inpath, updatecheck)
ride = RIDE(inpath, updatecheck, settingspath=settingspath)
if wx.VERSION <= (4, 0, 4, '', ''):
_show_old_wxpython_warning_if_needed(ride.frame)
else:
Expand Down Expand Up @@ -139,7 +152,7 @@ def _show_old_wxpython_warning_if_needed(parent=None):
message = ("RIDE needs a newer wxPython version. Your current "
"version is %s."
"\n"
"At the time of this release the current wxPython version is 4.2.0. See "
"At the time of this release the current wxPython version is 4.2.1. See "
"https://wxPython.org/ for downloads and instructions."
% wx.VERSION_STRING)
style = wx.ICON_EXCLAMATION
Expand Down
6 changes: 6 additions & 0 deletions src/robotide/application/CHANGELOG.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>Changelog</title><link rel="stylesheet" type="text/css" href="docbook-xsl.css" /><meta name="generator" content="DocBook XSL Stylesheets Vsnapshot" /></head><body><div xml:lang="en" class="article" lang="en"><div class="titlepage"><div><div><h2 class="title"><a id="id1337"></a>Changelog</h2></div></div><hr /></div><p>All notable changes to this project will be documented in this file.</p><p>The format is based on <a class="ulink" href="http://keepachangelog.com/en/1.0.0/" target="_top">Keep a Changelog</a>
and this project adheres to <a class="ulink" href="http://semver.org/spec/v2.0.0.html" target="_top">Semantic Versioning</a>.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="_ulink_url_https_github_com_robotframework_ride_unreleased_ulink"></a>1. <a class="ulink" href="https://github.com/robotframework/RIDE" target="_top">Unreleased</a></h2></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a id="_added"></a>1.1. Added</h3></div></div></div><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">
Added command line option, ``--settingspath``, to select a different configuration. For example, you can have different
colour settings or UI language.
</li><li class="listitem">
Added different settings file, according the actual Python executable, if not the original installed.
This allows different configurations, like, for example, in a virtual environment
</li><li class="listitem">
Added context option ``Open Containing Folder`` to test suites directories in Project Explorer.
</li><li class="listitem">
Added a setting for a specific file manager by editing the settings.cfg file. Add the string parameter ``file manager`` in the section ``[General]``.
Expand Down
5 changes: 3 additions & 2 deletions src/robotide/application/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,10 @@ class RIDE(wx.App):
settings = None
treeplugin = None

def __init__(self, path=None, updatecheck=True):
def __init__(self, path=None, updatecheck=True, settingspath=None):
self._updatecheck = updatecheck
self.workspace_path = path
self.settings_path = settingspath
context.APP = self
wx.App.__init__(self, redirect=False)

Expand All @@ -90,7 +91,7 @@ def OnInit(self): # Overrides wx method
self._locale = wx.Locale(wx.LANGUAGE_ENGLISH_US) # LANGUAGE_PORTUGUESE
# Needed for SetToolTipString to work
wx.HelpProvider.Set(wx.SimpleHelpProvider()) # DEBUG: adjust to wx versions
self.settings = RideSettings()
self.settings = RideSettings(self.settings_path)

class Message:
keys = ['General']
Expand Down
9 changes: 7 additions & 2 deletions src/robotide/application/releasenotes.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ def set_content(self, html_win, content):
<li>This version supports Python 3.8 up to 3.12.</li>
<li>There are some changes, or known issues:<ul>
<li>❌ - Removed support for Python 3.6 and 3.7</li>
<li>✔ - Added command line option, <b>--settingspath</b>, to select a different configuration.</li>
<li>✔ - Added different settings file, according the actual Python executable, if not the original installed.</li>
<li>✔ - Added a selector for Tasks and Language to the New Project dialog.</li>
<li>✔ - Added UI localization prepared for all the languages from installed Robot Framework version 6.1, or
higher. Major translations are: Dutch, Portuguese and Brazilian Portuguese. Still missing translation
Expand All @@ -182,6 +184,8 @@ def set_content(self, html_win, content):
</ul>
<p><strong>New Features and Fixes Highlights</strong></p>
<ul class="simple">
<li>Added command line option, <b>--settingspath</b>, to select a different configuration.</li>
<li>Added different settings file, according the actual Python executable, if not the original installed.</li>
<li>Fixed headers and blank spacing in Templated tests.</li>
<li>Added context option <b>Open Containing Folder</b> to test suites directories in Project Explorer.</li>
<li>Added a setting for a specific file manager by editing the settings.cfg file. Add the string parameter <b>file manager</b>
Expand Down Expand Up @@ -285,8 +289,9 @@ def set_content(self, html_win, content):
<pre class="literal-block">
python -m robotide.postinstall -install
</pre>
<p>RIDE {VERSION} was released on 04/May/2024.</p>
<br/>
<p>RIDE {VERSION} was released on 06/May/2024.</p>
<!-- <br/>
<h3>May The Fourth Be With You!</h3>
-->
</div>
"""
2 changes: 2 additions & 0 deletions src/robotide/context/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
IS_LINUX = sys.platform == 'linux'
WX_VERSION = wx.VERSION_STRING
IS_WX_410_OR_HIGHER = WX_VERSION >= '4.1.0'
EXECUTABLE = sys.executable

if IS_WINDOWS:
SETTINGS_DIRECTORY = os.path.join(
Expand Down Expand Up @@ -88,6 +89,7 @@ def get_about_ride():

return "".join(build_about)


"""
ABOUT_RIDE = '''<h3>RIDE -- Robot Framework Test Data Editor</h3>
<p>RIDE %s running on Python %s.</p>
Expand Down
19 changes: 17 additions & 2 deletions src/robotide/preferences/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import os
import shutil

from ..context import SETTINGS_DIRECTORY, LIBRARY_XML_DIRECTORY
from ..context import SETTINGS_DIRECTORY, LIBRARY_XML_DIRECTORY, EXECUTABLE
from .configobj import ConfigObj, ConfigObjError, Section, UnreprError
from .excludes_class import Excludes
from ..publish import RideSettingsChanged
Expand All @@ -29,6 +29,8 @@
def initialize_settings(path, dest_file_name=None):
if not os.path.exists(SETTINGS_DIRECTORY):
os.makedirs(SETTINGS_DIRECTORY)
if not os.path.exists(path):
path = os.path.join(SETTINGS_DIRECTORY, path)
(path, error) = _copy_or_migrate_user_settings(
SETTINGS_DIRECTORY, path, dest_file_name)
if error:
Expand All @@ -44,6 +46,8 @@ def _copy_or_migrate_user_settings(settings_dir, source_path, dest_file_name):
is given.
"""
m_error = None
if not os.path.exists(source_path):
raise(FileNotFoundError(source_path))
if not dest_file_name:
dest_file_name = os.path.basename(source_path)
settings_path = os.path.join(settings_dir, dest_file_name)
Expand Down Expand Up @@ -357,7 +361,18 @@ def __init__(self, path=None):
Settings.__init__(self, user_path)
self._settings_dir = os.path.dirname(user_path)
# print("DEBUG: RideSettings, self._settings_dir %s\n" % self._settings_dir)
self.set('install root', os.path.dirname(os.path.dirname(__file__)))
self.get('install root', os.path.dirname(os.path.dirname(__file__)))
self.executable = self.get('executable', EXECUTABLE)
if self.executable != EXECUTABLE:
digest = 0
for c in EXECUTABLE:
digest += ord(c)
new_user_path = user_path.replace("settings.cfg", f"settings_{digest}.cfg")
new_user_path = initialize_settings(user_path, new_user_path)
Settings.__init__(self, new_user_path)
self._settings_dir = os.path.dirname(new_user_path)
self.set('install root', os.path.dirname(os.path.dirname(__file__)))
self.executable = self.set('executable', EXECUTABLE)

def get_path(self, *parts):
"""Returns path which combines settings directory and given parts."""
Expand Down
2 changes: 1 addition & 1 deletion src/robotide/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@
# limitations under the License.
#
# Automatically generated by `tasks.py`.
VERSION = 'v2.1dev32'
VERSION = 'v2.1dev33'
29 changes: 17 additions & 12 deletions utest/application/test_app_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,16 +81,21 @@ def test_main_call_with_fail_version(self):

def test_parse_args(self):
from robotide import _parse_args
noupdatecheck, debug_console, inpath = _parse_args(args=None)
assert (noupdatecheck, debug_console, inpath) == (False, False, None)
noupdatecheck, debug_console, inpath = _parse_args(args=('--noupdatecheck', 'no file'))
assert (noupdatecheck, debug_console, inpath) == (True, False, 'no file')
noupdatecheck, debug_console, inpath = _parse_args(args=('--noupdatecheck', '--debugconsole'))
assert (noupdatecheck, debug_console, inpath) == (True, True, None)
noupdatecheck, debug_console, inpath = _parse_args(args='')
assert (noupdatecheck, debug_console, inpath) == (False, False, None)
noupdatecheck, debug_console, inpath = _parse_args(args=('--garbagein', '--garbageout'))
assert (noupdatecheck, debug_console, inpath) == (False, False, '--garbageout') # returns always last arg
noupdatecheck, debug_console, settings_path, inpath = _parse_args(args=None)
assert (noupdatecheck, debug_console, settings_path, inpath) == (False, False, None, None)
noupdatecheck, debug_console, settings_path, inpath = _parse_args(args=('--noupdatecheck', 'no file'))
assert (noupdatecheck, debug_console, settings_path, inpath) == (True, False, None, 'no file')
noupdatecheck, debug_console, settings_path, inpath = _parse_args(args=('--noupdatecheck', '--debugconsole'))
assert (noupdatecheck, debug_console, settings_path, inpath) == (True, True, None, None)
noupdatecheck, debug_console, settings_path, inpath = _parse_args(args=('--noupdatecheck', '--debugconsole',
'--settingspath', 'mysettings.cfg',
'my_test_suite'))
assert (noupdatecheck, debug_console, settings_path, inpath) == (True, True, 'mysettings.cfg', 'my_test_suite')
noupdatecheck, debug_console, settings_path, inpath = _parse_args(args='')
assert (noupdatecheck, debug_console, settings_path, inpath) == (False, False, None, None)
noupdatecheck, debug_console, settings_path, inpath = _parse_args(args=('--garbagein', '--garbageout'))
# returns always first arg
assert (noupdatecheck, debug_console, settings_path, inpath) == (False, False, None, '--garbagein')

def test_run_call_with_fail_import(self):
import robotide.application
Expand Down Expand Up @@ -133,7 +138,7 @@ def MainLoop(self): # Overrides wx method
m.setattr(wx, 'VERSION', (4, 0, 0, '', ''))
from wx import MessageDialog
m.setattr(MessageDialog, 'ShowModal', my_show)
robotide._run(False, False)
robotide._run(False, False, False, None)

def test_run_call_with_new_version_dbg_console(self):
import robotide.application
Expand Down Expand Up @@ -169,7 +174,7 @@ def MainLoop(self):
m.setattr(robotide.application, 'RIDE', SideEffect)
import wx
m.setattr(wx, 'VERSION', (4, 4, 0, '', ''))
robotide._run(False, False, True)
robotide._run(False, False, True, None)

def test_main_call_with_fail_run(self):
import robotide
Expand Down
22 changes: 20 additions & 2 deletions utest/run/test_argumentparsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ class ArgumentParsingTestCase(unittest.TestCase):
def test_no_args(self):
self._assert_args([])

def _assert_args(self, tested, expected_no_update_check=False, expected_debug_console=False, expected_path=None):
self.assertEqual((expected_no_update_check, expected_debug_console, expected_path), robotide._parse_args(tested))
def _assert_args(self, tested, expected_no_update_check=False, expected_debug_console=False,
expect_settings_path=None, expected_path=None):
self.assertEqual((expected_no_update_check, expected_debug_console, expect_settings_path, expected_path),
robotide._parse_args(tested))

def test_path_to_data(self):
self._assert_args(['data'], expected_path='data')
Expand All @@ -40,6 +42,22 @@ def test_debugconsole(self):
def test_debugconsole_and_path(self):
self._assert_args(['--debugconsole', 'dir'], expected_debug_console=True, expected_path='dir')

def test_settingspath(self):
self._assert_args(['--settingspath'], expect_settings_path=None)

def test_settingspath_and_filename(self):
self._assert_args(['--settingspath', 'my_settings.cfg'],
expect_settings_path='my_settings.cfg')

def test_settingspath_and_path(self):
self._assert_args(['--settingspath', '/tmp/.robotide/ride/my_settings.cfg'],
expect_settings_path='/tmp/.robotide/ride/my_settings.cfg')

def test_settingspath_path_and_testsuite(self):
self._assert_args(['--settingspath', '/tmp/.robotide/ride/my_settings.cfg', 'dir/testsuite.robot'],
expect_settings_path='/tmp/.robotide/ride/my_settings.cfg',
expected_path='dir/testsuite.robot')


if __name__ == '__main__':
unittest.main()
14 changes: 14 additions & 0 deletions utest/settings/test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import unittest
import os

import pytest

from robotide.preferences import settings
from robotide.preferences.settings import Settings, SectionError,\
ConfigurationError, initialize_settings, SettingsMigrator
Expand Down Expand Up @@ -296,6 +298,7 @@ def setUp(self):
self._orig_dir = settings.SETTINGS_DIRECTORY
self.settings_dir = os.path.join(os.path.dirname(__file__), 'ride')
# print("DEBUG: Settings dir init %s" % self.settings_dir)

settings.SETTINGS_DIRECTORY = self.settings_dir
self._init_settings_paths()
self._write_settings("foo = 'bar'\nhello = 'world'\n",
Expand All @@ -306,6 +309,8 @@ def tearDown(self):
settings.SETTINGS_DIRECTORY = self._orig_dir
self._remove_path(self.user_settings_path)
self._remove_path((self.user_settings_path+'_old_broken'))
self._remove_path(os.path.join(self.settings_dir, 'new_settings.cfg'))
self._remove_path(os.path.join(self.settings_dir, 'my_settings.cfg'))
os.removedirs(self.settings_dir)

def test_initialize_settings_creates_directory(self):
Expand All @@ -316,6 +321,15 @@ def test_initialize_settings_copies_settings(self):
initialize_settings(self.settings_path, 'user.cfg')
self.assertTrue(os.path.exists(self.settings_dir))

def test_initialize_settings_raises_error_if_no_source(self):
with pytest.raises(FileNotFoundError):
initialize_settings('no_settings.cfg', 'user.cfg')

def test_initialize_settings_builds_default_source_path(self):
initialize_settings(self.settings_path, 'new_settings.cfg')
path = initialize_settings('new_settings.cfg', 'my_settings.cfg')
assert path == os.path.join(settings.SETTINGS_DIRECTORY, 'my_settings.cfg')

def test_initialize_settings_does_merge_when_settings_exists(self):
os.mkdir(self.settings_dir)
self._write_settings(
Expand Down

0 comments on commit d6e2f2e

Please sign in to comment.