AUTHORS | 1 + COPYING | 339 +++++++++++++++++++++ README_dev.md | 4 + pyi-hooks/hook-tionix_vdi_client.py | 4 +- requirements.txt | 5 +- run_tests.py | 10 +- setup.py | 9 +- setup_distributive.py | 10 +- setup_win.py | 34 +-- tionix_vdi_client/__init__.py | 26 +- tionix_vdi_client/app_cli.py | 12 +- tionix_vdi_client/app_gui.py | 46 +-- tionix_vdi_client/client.py | 92 +++--- tionix_vdi_client/diagnostics.py | 10 +- tionix_vdi_client/exceptions.py | 70 ++--- tionix_vdi_client/gui/application.py | 6 +- tionix_vdi_client/gui/main_window.py | 6 +- tionix_vdi_client/gui/messagebox.py | 2 +- tionix_vdi_client/gui/password_window.py | 22 +- tionix_vdi_client/gui/pin_window.py | 6 +- tionix_vdi_client/gui/request_window.py | 39 ++- tionix_vdi_client/gui/resources_rc.py | 2 +- tionix_vdi_client/gui/settings_window.py | 24 +- tionix_vdi_client/gui/ui_main.py | 4 +- tionix_vdi_client/gui/ui_password.py | 4 +- tionix_vdi_client/gui/ui_pin.py | 4 +- tionix_vdi_client/gui/ui_request.py | 4 +- tionix_vdi_client/gui/ui_settings.py | 4 +- tionix_vdi_client/helpers.py | 16 +- tionix_vdi_client/i18n.py | 8 +- tionix_vdi_client/identity.py | 6 +- tionix_vdi_client/log.py | 4 +- tionix_vdi_client/main.py | 4 +- tionix_vdi_client/rdp.py | 30 +- tionix_vdi_client/settings.py | 173 ++++++----- tionix_vdi_client/smart_card/__init__.py | 2 +- tionix_vdi_client/smart_card/api.py | 68 ++--- tionix_vdi_client/smart_card/pcsc.py | 16 +- .../func_test_vdi_client_check_config_files.py | 40 +-- .../func_test_vdi_client_check_localization.py | 4 +- .../func_test_vdi_client_server_connection.py | 8 +- tionix_vdi_client/tests/functional/runner.py | 22 +- .../test_diagnostic/test_vdi_client_conf_files.py | 2 +- tionix_vdi_client/tests/unit/test_vdi_connect.py | 8 +- tionix_vdi_client/tests/unit/test_writers.py | 2 +- tionix_vdi_client/version_writter.py | 4 +- tionix_vdi_client/win/devices.py | 2 +- tionix_vdi_client/win/winapi.py | 2 +- 48 files changed, 782 insertions(+), 438 deletions(-) diff --git a/AUTHORS b/AUTHORS index e9148d6..c9b8635 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,3 +1,4 @@ +Andrey Cherepanov Artem Vasiliev Bagautdinov Aynur Camilla diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/README_dev.md b/README_dev.md index 4e8b789..07433ff 100644 --- a/README_dev.md +++ b/README_dev.md @@ -72,3 +72,7 @@ To debug VDI.client builds on Windows, remove `--widowed` option from pyinstalle # Requirements for the end-user Linux machine * No requirements + +## License + +This software is licensed under the terms of GPLv2, see COPYING file for details. diff --git a/pyi-hooks/hook-tionix_vdi_client.py b/pyi-hooks/hook-tionix_vdi_client.py index 2a68e10..7cdbc0d 100644 --- a/pyi-hooks/hook-tionix_vdi_client.py +++ b/pyi-hooks/hook-tionix_vdi_client.py @@ -40,5 +40,5 @@ for filepath in cythonized: hiddenimports.append(alias.name) -print 'Parsed tionix_vdi_client hidden imports:' -print '\n'.join(hiddenimports) +print('Parsed tionix_vdi_client hidden imports:') +print('\n'.join(hiddenimports)) diff --git a/requirements.txt b/requirements.txt index 60b65fb..0f9a968 100755 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,9 @@ pbr!=2.1.0,>=2.0.0 requests!=2.12.2,!=2.13.0,>=2.10.0 prettytable -cliff>=1.16.0 -PySide2 +PyQt5 # smart card functional pykcs11==1.4.3 pyopenssl -pyasn1 \ No newline at end of file +pyasn1 diff --git a/run_tests.py b/run_tests.py index 0fd5519..2e3a99b 100644 --- a/run_tests.py +++ b/run_tests.py @@ -55,15 +55,15 @@ def collect_error_codes(): settings.CONF.language = 'en' switch_language('en_US.UTF-8') from tionix_vdi_client import exceptions - for code, message in exceptions.ErrorCodes.MESSAGES.items(): + for code, message in list(exceptions.ErrorCodes.MESSAGES.items()): error_codes[code] = {'en': message} # collect russian errors description settings.CONF.language = 'ru' switch_language('ru_RU.UTF-8') reload(exceptions) - for code, message in exceptions.ErrorCodes.MESSAGES.items(): - error_codes[code]['ru'] = message.encode('utf-8') + for code, message in list(exceptions.ErrorCodes.MESSAGES.items()): + error_codes[code]['ru'] = message.encode().decode('utf-8') with open(ERROR_CODES_PATH, 'w') as fh: json.dump(error_codes, fh, indent=2, separators=(',', ': '), ensure_ascii=False) @@ -84,8 +84,8 @@ def compile_messages(): os.chdir(BASE_DIR) retcode = subprocess.call(['make', 'compilemessages']) if retcode: - raise RuntimeError(u"Failed to compile messages." - u"Return code: {}.".format(retcode)) + raise RuntimeError("Failed to compile messages." + "Return code: {}.".format(retcode)) if __name__ == '__main__': diff --git a/setup.py b/setup.py index 9b3ea64..a4145f1 100755 --- a/setup.py +++ b/setup.py @@ -1,5 +1,5 @@ # coding: utf-8 -from __future__ import print_function + import os import sys @@ -12,8 +12,8 @@ if 'sdist' in sys.argv or 'dist' in sys.argv: try: write_version_to_file() except Exception as err: - print(u'Error while defining TIONIX.VDIclient ' - u'version. {0}'.format(err)) + print('Error while defining TIONIX.VDIclient ' + 'version. {0}'.format(err)) exit(os.EX_SOFTWARE) with open('requirements.txt') as f: @@ -34,8 +34,7 @@ def get_classifiers(): 'Intended Audience :: System Administrators', 'Operating System :: POSIX :: Linux', 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', ] diff --git a/setup_distributive.py b/setup_distributive.py index 0c823b3..09306c8 100644 --- a/setup_distributive.py +++ b/setup_distributive.py @@ -39,7 +39,7 @@ pyc_files = [] # noinspection PyAttributeOutsideInit class CustomBuildExt(build_ext): def run(self): - print u'Compiling sources to *.so files:' + print('Compiling sources to *.so files:') self.inplace = True # pylint: disable=attribute-defined-outside-init build_ext.run(self) self.post_build() @@ -47,13 +47,13 @@ class CustomBuildExt(build_ext): @staticmethod def post_build(): # Compile py to pyc - print u'Compiling *.py files to *.pyc:' + print('Compiling *.py files to *.pyc:') for py_file in pyc_files: - print u'compiling: {0}'.format(py_file) + print('compiling: {0}'.format(py_file)) py_compile.compile(py_file, doraise=True) # Remove unnecessary pyc files - print u'Removing unecessary *.pyc files:' + print('Removing unecessary *.pyc files:') for root, _dirs, files in os.walk(PACKAGE_NAME): for f in files: base, ext = os.path.splitext(f) @@ -62,7 +62,7 @@ class CustomBuildExt(build_ext): py_file = os.path.join(root, base + PY) if py_file not in pyc_files: pyc_file = os.path.join(root, f) - print u'delete: {0}'.format(pyc_file) + print('delete: {0}'.format(pyc_file)) os.remove(pyc_file) diff --git a/setup_win.py b/setup_win.py index 07354fd..0c0fbc4 100644 --- a/setup_win.py +++ b/setup_win.py @@ -17,7 +17,7 @@ from tionix_vdi_client.version_writter import write_version_to_file try: version, os_bitness = write_version_to_file() except Exception as err: - print u'Error while defining TIONIX.VDIclient version. {0}'.format(err) + print('Error while defining TIONIX.VDIclient version. {0}'.format(err)) exit(1) DEBUG = False @@ -58,43 +58,43 @@ def exclude_module(modules): # noinspection PyShadowingNames def build_runner_exe(): exclude = ['tkinter', '_tkinter'] - cmd = (u'pyinstaller --onefile --windowed --distpath {distpath} -n {name} ' - u'--clean -i {icon} {add_data} {exclude_module} ' - u'--additional-hooks-dir=pyi-hooks/ ' - u'{script_name}'.format( + cmd = ('pyinstaller --onefile --windowed --distpath {distpath} -n {name} ' + '--clean -i {icon} {add_data} {exclude_module} ' + '--additional-hooks-dir=pyi-hooks/ ' + '{script_name}'.format( distpath=BUILD_DIR, name=TARGET_NAME, icon=TIONIX_ICON, add_data=add_data(), exclude_module=exclude_module(exclude), script_name=RUNNER_SCRIPT_NAME)) - print(u'Running command:', cmd) + print(('Running command:', cmd)) try: popen = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE) popen.communicate() except OSError as err: - print( - u'Command failed: {cmd} with error {err}'.format(cmd=cmd, err=err)) + print(( + 'Command failed: {cmd} with error {err}'.format(cmd=cmd, err=err))) # noinspection PyShadowingNames def build_run_self_diagnostics_exe(): exclude = ['qt5', 'tkinter', '_tkinter', 'PySide2'] - cmd = (u'pyinstaller --onefile --distpath {distpath} -n {name} ' - u'--clean -i {icon} {add_data} {exclude_module} ' - u'--additional-hooks-dir=pyi-hooks/ ' - u'{script_name}'.format( + cmd = ('pyinstaller --onefile --distpath {distpath} -n {name} ' + '--clean -i {icon} {add_data} {exclude_module} ' + '--additional-hooks-dir=pyi-hooks/ ' + '{script_name}'.format( distpath=BUILD_DIR, name=DIAGNOSTIC_UTIL_NAME, icon=TIONIX_ICON, add_data=add_data(), exclude_module=exclude_module(exclude), script_name=SELF_DIAGNOSTIC_SCRIPT_NAME)) - print(u'Running command:', cmd) + print(('Running command:', cmd)) try: popen = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE) popen.communicate() except OSError as err: - print( - u'Command failed: {cmd} with error {err}'.format(cmd=cmd, err=err)) + print(( + 'Command failed: {cmd} with error {err}'.format(cmd=cmd, err=err))) def copy_localization(): @@ -192,8 +192,8 @@ class DistEXE(BuildEXE): break if not result_path: - print(u"Installer build failed: " - u"unable to find Inno Setup utility") + print("Installer build failed: " + "unable to find Inno Setup utility") exit(1) return result_path diff --git a/tionix_vdi_client/__init__.py b/tionix_vdi_client/__init__.py index 1d34e55..4da358b 100755 --- a/tionix_vdi_client/__init__.py +++ b/tionix_vdi_client/__init__.py @@ -12,8 +12,8 @@ BASE_DIR = os.path.dirname(__file__) try: from tionix_vdi_client.version import __version__, __os_bitness__ except ImportError: - __version__ = u'-' - __os_bitness__ = u'' + __version__ = '-' + __os_bitness__ = '' MODULE_VERSION = __version__ WIN_OS_BITNESS = __os_bitness__ @@ -40,7 +40,7 @@ def handle_exception(exc_type, exc_value, exc_traceback): if threading.current_thread().__class__.__name__ == '_MainThread': try: - from PySide2.QtWidgets import QApplication + from PyQt5.QtWidgets import QApplication from tionix_vdi_client.gui.messagebox import detailed_error from tionix_vdi_client.log import get_log_msg from tionix_vdi_client.exceptions import ErrorCodes @@ -51,7 +51,7 @@ def handle_exception(exc_type, exc_value, exc_traceback): detailed_error(None, title=_('Error'), text=msg, detailed=get_log_msg()) except Exception as err: - logger.error(u'Failed to show error messagebox: %s:%s', + logger.error('Failed to show error messagebox: %s:%s', err.__class__.__name__, err) @@ -59,17 +59,17 @@ sys.excepthook = handle_exception CONNECTION_TYPE = 'client' -CLIENT = u'TIONIX.VDIclient {0}'.format(MODULE_VERSION) +CLIENT = 'TIONIX.VDIclient {0}'.format(MODULE_VERSION) EX_OK = 0 EX_SOFTWARE = 1 class Localization(object): - EN = u'en' - RU = u'ru' + EN = 'en' + RU = 'ru' - RU_CODE = u'ru_RU' - EN_US_CODE = u'en_US' + RU_CODE = 'ru_RU' + EN_US_CODE = 'en_US' locales_dict = { RU: RU_CODE, @@ -77,21 +77,21 @@ class Localization(object): } locales_translate = { - RU: u'Русский', - EN: u'English', + RU: 'Русский', + EN: 'English', } # pylint: disable=inconsistent-return-statements @classmethod def get_code_by_locale(cls, locale): - for key, value in cls.locales_translate.items(): + for key, value in list(cls.locales_translate.items()): if value == locale: return key # pylint: disable=inconsistent-return-statements @classmethod def get_lang_by_code(cls, code): - for key, value in cls.locales_dict.items(): + for key, value in list(cls.locales_dict.items()): if value == code: return key return cls.EN diff --git a/tionix_vdi_client/app_cli.py b/tionix_vdi_client/app_cli.py index bacf517..419df3d 100644 --- a/tionix_vdi_client/app_cli.py +++ b/tionix_vdi_client/app_cli.py @@ -33,7 +33,7 @@ def main(): try: vdi_client = client.VdiClient(**conn_credentials) except exceptions.MissingParameterException as err: - logger.error(unicode(err)) + logger.error(str(err)) return EX_SOFTWARE try: @@ -64,18 +64,18 @@ def main(): ErrorCodes.MESSAGES.get(ErrorCodes.UNEXPECTED_ERROR)) logger.error(message) return EX_SOFTWARE - msg = _(u"The password needs to be changed.") + msg = _("The password needs to be changed.") logger.info(msg) new_password = getpass.getpass(prompt="New password: ") confirm_new_password = getpass.getpass(prompt="Confirm new password: ") if not new_password: - logger.error(_(u'Password could not be empty.')) + logger.error(_('Password could not be empty.')) return EX_SOFTWARE if new_password != confirm_new_password: - logger.error(_(u'Passwords do not match. ' - u'Make sure they are the same.')) + logger.error(_('Passwords do not match. ' + 'Make sure they are the same.')) return EX_SOFTWARE try: @@ -84,7 +84,7 @@ def main(): logger.error(err, exc_info=TRACEBACK_ENABLED) return EX_SOFTWARE else: - msg = _(u"Password successfully changed.") + msg = _("Password successfully changed.") logger.info(msg) except exceptions.ApplicationLogicException as err: return err.exit_code diff --git a/tionix_vdi_client/app_gui.py b/tionix_vdi_client/app_gui.py index 62e98b0..f23a281 100644 --- a/tionix_vdi_client/app_gui.py +++ b/tionix_vdi_client/app_gui.py @@ -1,13 +1,13 @@ # coding: utf-8 # pylint: disable=c-extension-no-member,no-name-in-module,import-error -from __future__ import unicode_literals + import atexit import logging import sys -from PySide2 import QtCore -from PySide2.QtWidgets import QMessageBox +from PyQt5 import QtCore +from PyQt5.QtWidgets import QMessageBox from tionix_vdi_client import EX_SOFTWARE, EX_OK, Localization from tionix_vdi_client.gui.application import QSingleApplication @@ -44,14 +44,14 @@ def main(): if CONF.use_cert: if CONF.use_smartcard: QMessageBox.critical( - None, _(u'Authentication'), + None, _('Authentication'), "Authentication by client certificate stored on the " "smartcard is not supported") return EX_SOFTWARE if not all([CONF.cert, CONF.key, CONF.identity_url]): QMessageBox.critical( - None, _(u'Authentication'), + None, _('Authentication'), "One of required configuraton options " "are missing: cert, key, identity_url") return EX_SOFTWARE @@ -60,11 +60,11 @@ def main(): token = request_token(CONF.identity_url, CONF.cert, CONF.key) except ApplicationConnectionError as err: QMessageBox.critical( - None, _(u'Authentication'), err.message) + None, _('Authentication'), str(err)) return EX_SOFTWARE if not token: QMessageBox.critical( - None, _(u'Authentication'), "Authentication failed") + None, _('Authentication'), "Authentication failed") return EX_SOFTWARE SESSION.token = token SESSION.token_auth = True @@ -73,7 +73,7 @@ def main(): return EX_OK if CONF.use_smartcard: - error_title = _(u'Smart card authentication') + error_title = _('Smart card authentication') try: if not smartcard_api: raise smartcard_exc.SmartCardNotConfigured() @@ -85,7 +85,7 @@ def main(): pin_attempts = smartcard_api.get_pin_attempts() if pin_attempts == 0: QMessageBox.critical( - None, _(u'Smart card authentication'), + None, _('Smart card authentication'), _("PIN tries exceeded. Smart card is locked.")) return EX_SOFTWARE @@ -93,21 +93,21 @@ def main(): min_len=session.min_pin_len, max_len=session.max_pin_len) if pin is None: - logger.warning(u'Smart card authentication aborted.') + logger.warning('Smart card authentication aborted.') return EX_SOFTWARE try: username, password = smartcard_api.get_session_data( - session, pin.encode('utf-8')) + session, pin.encode().decode('utf-8')) except IncorrectPin as err: QMessageBox.critical( None, error_title, - err.message) + str(err)) continue break if not password: reason = _( - u'No saved password found, please enter a new password.') + 'No saved password found, please enter a new password.') password = PasswordWindow.change_password(reason=reason) if not password: return EX_SOFTWARE @@ -119,28 +119,28 @@ def main(): except smartcard_exc.CertificateNotFound: QMessageBox.critical( None, error_title, - _(u'Unable to find any certificate on the smart card. ' - u'Please contact system administrator.')) + _('Unable to find any certificate on the smart card. ' + 'Please contact system administrator.')) return EX_SOFTWARE except smartcard_exc.AppropriateCertificateNotFound: QMessageBox.critical( None, error_title, - _(u'Unable to find appropriate certificate ' - u'on the smart card. ' - u'Please contact system administrator.')) + _('Unable to find appropriate certificate ' + 'on the smart card. ' + 'Please contact system administrator.')) return EX_SOFTWARE except smartcard_exc.MultipleCertificatesFound: QMessageBox.critical( None, error_title, - _(u'Found more than one appropriate certificate. ' - u'Please contact system administrator.')) + _('Found more than one appropriate certificate. ' + 'Please contact system administrator.')) return EX_SOFTWARE except smartcard_exc.SmartCardError: detailed_error( None, error_title, - _(u"Environment is not configured. " - u"Unable to get user login and password from smart card. " - u"Please contact system administrator."), + _("Environment is not configured. " + "Unable to get user login and password from smart card. " + "Please contact system administrator."), detailed=get_log_msg()) else: window = InstanceRequestWindow() diff --git a/tionix_vdi_client/client.py b/tionix_vdi_client/client.py index 60aeada..f0a6faa 100644 --- a/tionix_vdi_client/client.py +++ b/tionix_vdi_client/client.py @@ -17,7 +17,7 @@ from tionix_vdi_client.settings import CONF logger = logging.getLogger(__name__) -UNEXPECTED_ERR_MSG = _(u"Unexpected error occurred.") +UNEXPECTED_ERR_MSG = _("Unexpected error occurred.") VM = namedtuple('VM', 'info status rdp_token') @@ -33,28 +33,28 @@ class VdiClient(object): } else: self.auth_params = { - u'user': user, - u'password': password + 'user': user, + 'password': password } if domain_name: - self.auth_params[u'user_domain_name'] = domain_name + self.auth_params['user_domain_name'] = domain_name if project: self.auth_params.update({ - u'auth_tenant': project, - u'project_domain_name': domain_name, + 'auth_tenant': project, + 'project_domain_name': domain_name, }) self.client_params = {} if ip_address: - self.client_params[u'ip_address'] = ip_address + self.client_params['ip_address'] = ip_address elif CONF.ikecfg: - self.client_params[u'ip_address'] = CONF.ikecfg + self.client_params['ip_address'] = CONF.ikecfg self.client_params.update({ - u'conn_type': CONNECTION_TYPE, - u'os': get_os_version(), - u'client': CLIENT, + 'conn_type': CONNECTION_TYPE, + 'os': get_os_version(), + 'client': CLIENT, }) self.token = token @@ -96,16 +96,16 @@ class VdiClient(object): raise exceptions.ThreadCancelError for api_url in api_urls: - logger.info(u'Performing %s action on %s', action, api_url) + logger.info('Performing %s action on %s', action, api_url) if not web_guard: - url = u'{0}/{1}/{2}/'.format(api_url, API_VERSION, action) + url = '{0}/{1}/{2}/'.format(api_url, API_VERSION, action) else: - url = u'{0}/{1}'.format(api_url, action) + url = '{0}/{1}'.format(api_url, action) try: - logger.debug(u'Requesting url {0} with parameters: {1}' - u''.format(url, body)) + logger.debug('Requesting url {0} with parameters: {1}' + ''.format(url, body)) if body: if not web_guard: response = requests.post(url, headers=headers, @@ -119,16 +119,16 @@ class VdiClient(object): response = requests.get(url, cookies=cookies, headers=headers) except requests.exceptions.SSLError: - msg = _(u"Server SSL certificate verification failed.") + msg = _("Server SSL certificate verification failed.") logger.error(msg) server_errs[api_url] = msg except requests.exceptions.ConnectionError: - msg = _(u"Server is not available. " - u"Check URL for connection.") + msg = _("Server is not available. " + "Check URL for connection.") logger.error(msg) server_errs[api_url] = msg except requests.exceptions.InvalidURL: - msg = _(u"Invalid URL. Check URL for connection.") + msg = _("Invalid URL. Check URL for connection.") logger.error(msg) server_errs[api_url] = msg else: @@ -143,7 +143,7 @@ class VdiClient(object): if err.code not in ( exceptions.ErrorCodes.retriable_errors): raise err - server_errs[api_url] = err.message + server_errs[api_url] = str(err) else: if result is not None: self.api_url = api_url @@ -152,14 +152,14 @@ class VdiClient(object): if retries > 0: retries -= 1 if retries > 0: - logger.info(u"Unable to connect to cloud. Tries left: {0}" - u"".format(retries)) + logger.info("Unable to connect to cloud. Tries left: {0}" + "".format(retries)) elif retries < 0: - logger.info(u"Unable to connect to cloud. Retry after {0} " - u"seconds.".format(CONF.timeout)) + logger.info("Unable to connect to cloud. Retry after {0} " + "seconds.".format(CONF.timeout)) if retries != 0: logger.info( - _(u"{0} seconds timeout before reconnecting to the cloud.") + _("{0} seconds timeout before reconnecting to the cloud.") .format(CONF.timeout)) if stop_event: stop_event.wait(CONF.timeout) @@ -167,14 +167,14 @@ class VdiClient(object): time.sleep(CONF.timeout) msg = ungettext( - u"Unable to connect to server: {}", - u"Unable to connect to servers: {}", + "Unable to connect to server: {}", + "Unable to connect to servers: {}", len(api_urls)) fail_reason_msgs = [ - _(u"{server_url}\nReason: {err}").format(server_url=url, err=err) - for url, err in server_errs.items()] + _("{server_url}\nReason: {err}").format(server_url=url, err=err) + for url, err in list(server_errs.items())] raise exceptions.ApplicationConnectionError( - msg.format(u"\n\n".join(fail_reason_msgs))) + msg.format("\n\n".join(fail_reason_msgs))) def web_guard_request(self, stop_event=None): """ @@ -200,11 +200,11 @@ class VdiClient(object): request_id = response.get('request_id') if not request_id: - msg = _(u"Couldn't get request id from server response.") - logger.error(u"Couldn't get request_id from json: %s", response) + msg = _("Couldn't get request id from server response.") + logger.error("Couldn't get request_id from json: %s", response) raise exceptions.VDIServerResponseError(msg) - logger.info(u"Successfully connected to %s.", self.api_url) - logger.debug(u"Got request_id: %s", request_id) + logger.info("Successfully connected to %s.", self.api_url) + logger.debug("Got request_id: %s", request_id) return request_id def get_vm(self, request_id, stop_event=None): @@ -212,7 +212,7 @@ class VdiClient(object): Connect to server and retrieve vm info :returns: vm data """ - data = {u'request': request_id} + data = {'request': request_id} response = self._action('get-vm', stop_event=stop_event, body=data) info = response.get('server') status = response.get('status') @@ -225,14 +225,14 @@ class VdiClient(object): self.web_guard_request(stop_event=stop_event) data = { - u'new_password': new_password + 'new_password': new_password } data.update(**self.auth_params) data.pop('auth_tenant', None) data.pop('project_domain_name', None) self._action('change-password', body=data, stop_event=stop_event) - self.auth_params[u'password'] = new_password + self.auth_params['password'] = new_password @staticmethod def _get_response_json(response): @@ -240,12 +240,12 @@ class VdiClient(object): try: json_body = response.json() except ValueError: - msg = _(u"Response parsing error.") + msg = _("Response parsing error.") logger.error( - u"Response doesn't have json. Request url: %s", response.url) + "Response doesn't have json. Request url: %s", response.url) raise exceptions.VDIServerResponseError(msg) - logger.debug(u'Got following response: {0}'.format(json_body)) + logger.debug('Got following response: {0}'.format(json_body)) return json_body def _parse_response(self, response, cloud=None): @@ -253,12 +253,12 @@ class VdiClient(object): self.token = response.headers.get('X-Auth-Token') or self.token if response.status_code in (500, 503): - logger.error(u"Error %s. Url: %s." - u"See VDIserver logs for more details.", + logger.error("Error %s. Url: %s." + "See VDIserver logs for more details.", response.status_code, response.url) elif response.status_code >= 400: response_content = response.content.decode('utf-8') - err_msg = u"Error %s. Content: %s" + err_msg = "Error %s. Content: %s" logger.error(err_msg, response.status_code, response_content) if response.status_code == 400: @@ -273,7 +273,7 @@ class VdiClient(object): def _parse_web_guard_response(self, response): if response.status_code >= 400: response_content = response.content.decode('utf-8') - err_msg = u"Error %s. Content: %s" + err_msg = "Error %s. Content: %s" logger.error(err_msg, response.status_code, response_content) raise exceptions.WebGuardConnectionError() else: @@ -288,4 +288,4 @@ class VdiClient(object): random_int = str(random.randint(0, 9)) concatenated = int(timestamp + random_int) shifted = str(concatenated >> 1) - return base64.b64encode(shifted) + return base64.b64encode(shifted.encode()) diff --git a/tionix_vdi_client/diagnostics.py b/tionix_vdi_client/diagnostics.py index c57f8c1..8ebb8c4 100644 --- a/tionix_vdi_client/diagnostics.py +++ b/tionix_vdi_client/diagnostics.py @@ -9,7 +9,7 @@ import sys from collections import defaultdict import prettytable -from cliff.utils import terminal_width +import subprocess # disable logger messages from imports logging.disable(logging.INFO) @@ -48,7 +48,7 @@ def adjust_columns_width(headers, rows, diff): if not isinstance(cell, (unicode, str)): cell = unicode(cell) if isinstance(cell, str): - cell = cell.decode('utf-8') + cell = cell.encode().decode('utf-8') cell_width = max(map(len, cell.splitlines() + [''])) columns_width[col_num] = max(columns_width[col_num], cell_width) @@ -85,7 +85,11 @@ def build_wrapped_table(headers=None, rows=()): table = build_table(headers, rows) table_width = len(str(table).splitlines()[0]) - max_width = terminal_width(sys.stdout) + columns = subprocess.getoutput('tput cols').strip() + try: + max_width = int(columns) + except: + max_width = 80 if max_width and max_width < table_width: # On Windows, table should be slightly smaller diff --git a/tionix_vdi_client/exceptions.py b/tionix_vdi_client/exceptions.py index 060e1da..9a38d76 100644 --- a/tionix_vdi_client/exceptions.py +++ b/tionix_vdi_client/exceptions.py @@ -12,7 +12,7 @@ from tionix_vdi_client.settings import CONF logger = logging.getLogger(__name__) -if CONF.language == u'ru': +if CONF.language == 'ru': SUPPORT_MESSAGE = CONF.contact_support_message_ru else: SUPPORT_MESSAGE = CONF.contact_support_message_en @@ -59,50 +59,50 @@ class ErrorCodes(object): MESSAGES = { UNEXPECTED_ERROR: _( - u"Unexpected error occurred. ") + SUPPORT_MESSAGE, + "Unexpected error occurred. ") + SUPPORT_MESSAGE, LICENSE_ERROR: _( - u'License of VDI server {cloud_url} got out. ') + SUPPORT_MESSAGE, + 'License of VDI server {cloud_url} got out. ') + SUPPORT_MESSAGE, NO_AVAILABLE_PROJECT: _( - u'No available VDI project found. ') + SUPPORT_MESSAGE, - AUTH_ERROR: _(u"Invalid credentials. ") + SUPPORT_MESSAGE, - PASSWORD_EXPIRED: _(u"The password needs to be changed."), - NON_VDI_PROJECT: _(u'Not a VDI project: {project_name}.'), + 'No available VDI project found. ') + SUPPORT_MESSAGE, + AUTH_ERROR: _("Invalid credentials. ") + SUPPORT_MESSAGE, + PASSWORD_EXPIRED: _("The password needs to be changed."), + NON_VDI_PROJECT: _('Not a VDI project: {project_name}.'), PROJECT_QUOTA_EXCEEDED: _( - u"Quota exceeded for VDI project " - u"\"{project_name}\". ") + SUPPORT_MESSAGE, + "Quota exceeded for VDI project " + "\"{project_name}\". ") + SUPPORT_MESSAGE, SERVER_CREATE_FAILED: _( - u'Unable to create instance in project {project_name}. ' + 'Unable to create instance in project {project_name}. ' ) + SUPPORT_MESSAGE, NO_INSTANCE_IN_PROJECT: _( - u'No instance found in project {project_name}. ' + 'No instance found in project {project_name}. ' ) + SUPPORT_MESSAGE, DOMAIN_NOT_FOUND: _( - u'Domain {domain_name} not found.'), + 'Domain {domain_name} not found.'), USER_NOT_FOUND: _( - u'User {user_name} not found in domain {domain_name}.'), + 'User {user_name} not found in domain {domain_name}.'), INSTANCE_STATE_ERROR: _( - u'Instance with id {instance_id} is ' - u'in invalid state: {instance_status}. ') + SUPPORT_MESSAGE, + 'Instance with id {instance_id} is ' + 'in invalid state: {instance_status}. ') + SUPPORT_MESSAGE, INSTANCE_NO_IP: _( - u'Unable to get IP address of instance with id {instance_id}. ' + 'Unable to get IP address of instance with id {instance_id}. ' ) + SUPPORT_MESSAGE, - PASSWORD_EXPIRE_WARNING: _(u'Password expires in {seconds} seconds ' - u'and needs to be changed.'), - PASSWORD_ATTEMPTS_WARNING: _(u'Password expired, {attempts} grace ' - u'logins remain.'), + PASSWORD_EXPIRE_WARNING: _('Password expires in {seconds} seconds ' + 'and needs to be changed.'), + PASSWORD_ATTEMPTS_WARNING: _('Password expired, {attempts} grace ' + 'logins remain.'), KEYSTONE_SERVER_ERROR: _( - u'Authentication server is not available. ') + SUPPORT_MESSAGE, + 'Authentication server is not available. ') + SUPPORT_MESSAGE, KERBEROS_SCRIPT_FILE_PROBLEM: _( - u"Couldn't change password because of the KERBEROS.script_path " - u"file doesn't exist or it's unaccessible. ") + SUPPORT_MESSAGE, + "Couldn't change password because of the KERBEROS.script_path " + "file doesn't exist or it's unaccessible. ") + SUPPORT_MESSAGE, KERBEROS_SCRIPT_RETURN_1: _( - u"Couldn't change password because of an error while change " - u"password process execution. ") + SUPPORT_MESSAGE, + "Couldn't change password because of an error while change " + "password process execution. ") + SUPPORT_MESSAGE, # prevent to show original token message to user # show it in console log only - INVALID_TOKEN: _(u"Unexpected error occurred. ") + SUPPORT_MESSAGE, - INAPPROPRIATE_WEB_GUARDED_REQUEST: _(u"Invalid settings of Web Guard. " - u"") + SUPPORT_MESSAGE, + INVALID_TOKEN: _("Unexpected error occurred. ") + SUPPORT_MESSAGE, + INAPPROPRIATE_WEB_GUARDED_REQUEST: _("Invalid settings of Web Guard. " + "") + SUPPORT_MESSAGE, } @@ -131,10 +131,10 @@ class VDIServerResponseError(VDIException): @classmethod def from_json(cls, json_str, cloud_url=None): error_obj = json.loads(json_str) - error = error_obj.get(u'error', {}) - code = error.get(u'code') - data = error.get(u'data') - message = error.get(u'message') + error = error_obj.get('error', {}) + code = error.get('code') + data = error.get('data') + message = error.get('message') if data is None: data = {} data['cloud_url'] = cloud_url @@ -179,9 +179,9 @@ class ApplicationConnectionError(VDIException): class WebGuardConnectionError(VDIException): - message = _(u"Unable to connect to Web Guard. ") + SUPPORT_MESSAGE + message = _("Unable to connect to Web Guard. ") + SUPPORT_MESSAGE class WebGuardCookieError(VDIException): - message = _(u"Unable to get session key from Web Guard. " - u"") + SUPPORT_MESSAGE + message = _("Unable to get session key from Web Guard. " + "") + SUPPORT_MESSAGE diff --git a/tionix_vdi_client/gui/application.py b/tionix_vdi_client/gui/application.py index 0d7ca24..bc9b90a 100644 --- a/tionix_vdi_client/gui/application.py +++ b/tionix_vdi_client/gui/application.py @@ -3,9 +3,9 @@ import os import sys import tempfile -from PySide2.QtCore import QIODevice -from PySide2.QtWidgets import QMessageBox, QApplication -from PySide2.QtNetwork import QLocalServer, QLocalSocket +from PyQt5.QtCore import QIODevice +from PyQt5.QtWidgets import QMessageBox, QApplication +from PyQt5.QtNetwork import QLocalServer, QLocalSocket # noinspection PyPep8Naming,PyAttributeOutsideInit,PyTypeChecker diff --git a/tionix_vdi_client/gui/main_window.py b/tionix_vdi_client/gui/main_window.py index 6468ccc..9ccb40d 100644 --- a/tionix_vdi_client/gui/main_window.py +++ b/tionix_vdi_client/gui/main_window.py @@ -4,8 +4,8 @@ import sys import threading from datetime import date -from PySide2 import QtCore, QtGui -from PySide2.QtWidgets import ( +from PyQt5 import QtCore, QtGui +from PyQt5.QtWidgets import ( QMainWindow, QDesktopWidget, QMessageBox, QPushButton, QSystemTrayIcon) from tionix_vdi_client import MODULE_VERSION @@ -133,7 +133,7 @@ class MainWindow(QMainWindow): def on_credentials_changed(self): required_params = self.get_auth_params() required_params.pop('project', None) - connect_enabled = all(required_params.values() + [CONF.cloud]) + connect_enabled = all(list(required_params.values()) + [CONF.cloud]) self.ui.button_connect.setEnabled(connect_enabled) def is_session_changed(self): diff --git a/tionix_vdi_client/gui/messagebox.py b/tionix_vdi_client/gui/messagebox.py index b523770..0d0411a 100644 --- a/tionix_vdi_client/gui/messagebox.py +++ b/tionix_vdi_client/gui/messagebox.py @@ -1,6 +1,6 @@ # coding: utf-8 # pylint: disable=c-extension-no-member,no-name-in-module,import-error -from PySide2.QtWidgets import QMessageBox +from PyQt5.QtWidgets import QMessageBox def detailed_error(parent=None, title=None, text=None, buttons=None, diff --git a/tionix_vdi_client/gui/password_window.py b/tionix_vdi_client/gui/password_window.py index 7c9eb32..df28079 100644 --- a/tionix_vdi_client/gui/password_window.py +++ b/tionix_vdi_client/gui/password_window.py @@ -2,8 +2,8 @@ # pylint: disable=c-extension-no-member,no-name-in-module,import-error import logging -from PySide2 import QtGui -from PySide2.QtWidgets import QDialog, QMessageBox, QLineEdit +from PyQt5 import QtGui +from PyQt5.QtWidgets import QDialog, QMessageBox, QLineEdit from tionix_vdi_client import exceptions from tionix_vdi_client.client import VdiClient @@ -148,16 +148,16 @@ class PasswordWindow(QDialog): confirm_new_password = self.ui.input_confirm_password.text() if new_password != confirm_new_password: - msg = _(u'Passwords do not match. Make sure they are the same.') - QMessageBox.information(self, _(u'Error'), msg) + msg = _('Passwords do not match. Make sure they are the same.') + QMessageBox.information(self, _('Error'), msg) return if CONF.password_generation: if not PasswordGenerator.check(new_password): - msg = _(u"The password must contain latin letters in upper " - u"(A-Z) and lower (a-z) case, at least 1 digit (0-9) " - u"and at least 1 special character (!@#$%^&*).") - QMessageBox.information(self, _(u'Error'), msg) + msg = _("The password must contain latin letters in upper " + "(A-Z) and lower (a-z) case, at least 1 digit (0-9) " + "and at least 1 special character (!@#$%^&*).") + QMessageBox.information(self, _('Error'), msg) self.init_password_inputs() return @@ -175,7 +175,7 @@ class PasswordWindow(QDialog): except (exceptions.VDIServerResponseError, exceptions.WebGuardCookieError, exceptions.WebGuardConnectionError) as err: - detailed_error(self, _(u'Connection error'), err.message, + detailed_error(self, _('Connection error'), str(err), detailed=get_log_msg()) self.init_password_inputs() else: @@ -185,10 +185,10 @@ class PasswordWindow(QDialog): result = smartcard_api.update_password( SESSION.smartcard_session, SESSION.user, new_password) if not result: - logger.warning(u'Error while updating smartcard password.') + logger.warning('Error while updating smartcard password.') QMessageBox.information( - self, '', _(u'Password successfully changed.')) + self, '', _('Password successfully changed.')) # close only on successful password change self.close() diff --git a/tionix_vdi_client/gui/pin_window.py b/tionix_vdi_client/gui/pin_window.py index d5fc7e9..aa64185 100644 --- a/tionix_vdi_client/gui/pin_window.py +++ b/tionix_vdi_client/gui/pin_window.py @@ -1,8 +1,8 @@ # coding: utf-8 # pylint: disable=c-extension-no-member,no-name-in-module,import-error -from PySide2 import QtGui -from PySide2.QtWidgets import QDialog, QLineEdit +from PyQt5 import QtGui +from PyQt5.QtWidgets import QDialog, QLineEdit from tionix_vdi_client.gui.ui_pin import Ui_PinWindow @@ -27,7 +27,7 @@ class PinWindow(QDialog): self.ui.label_attempts_count.setStyleSheet( 'QLabel {color: red; font-weight: bold}') attempts = attempts or '?' - self.ui.label_attempts_count.setText(unicode(attempts)) + self.ui.label_attempts_count.setText(str(attempts)) self.ui.input_pin.textChanged.connect(self.on_pin_changed) self.ui.input_pin.returnPressed.connect(self.on_ok_clicked) diff --git a/tionix_vdi_client/gui/request_window.py b/tionix_vdi_client/gui/request_window.py index a964445..889be2c 100644 --- a/tionix_vdi_client/gui/request_window.py +++ b/tionix_vdi_client/gui/request_window.py @@ -4,8 +4,8 @@ import json import logging import threading -from PySide2 import QtCore -from PySide2.QtWidgets import QDialog, QMessageBox +from PyQt5 import QtCore +from PyQt5.QtWidgets import QDialog, QMessageBox from tionix_vdi_client import exceptions from tionix_vdi_client.client import VdiClient @@ -31,11 +31,11 @@ ACTION_PERFOMED_MSG = _("{0} is performed for user's instance.") class RdpConnectionThread(QtCore.QThread): # OUT signals - connection_error = QtCore.Signal(str) - connection_success = QtCore.Signal() + connection_error = QtCore.pyqtSignal(str) + connection_success = QtCore.pyqtSignal() # IN signals - stop_requested = QtCore.Signal() + stop_requested = QtCore.pyqtSignal() def __init__(self): super(RdpConnectionThread, self).__init__() @@ -56,7 +56,7 @@ class RdpConnectionThread(QtCore.QThread): stop_event=self.stop_event, silent=CONF.silent) except Exception as err: - self.connection_error.emit(err.message) + self.connection_error.emit(str(err)) else: self.connection_success.emit() @@ -65,13 +65,13 @@ class RdpConnectionThread(QtCore.QThread): class InstanceRequestThread(QtCore.QThread): # OUT signals - instance_received = QtCore.Signal(str) - state_updated = QtCore.Signal(str) - response_error = QtCore.Signal(str) - request_error = QtCore.Signal(str) + instance_received = QtCore.pyqtSignal(str) + state_updated = QtCore.pyqtSignal(str) + response_error = QtCore.pyqtSignal(str) + request_error = QtCore.pyqtSignal(str) # IN signals - stop_requested = QtCore.Signal() + stop_requested = QtCore.pyqtSignal() # TODO: create vdi_client in thread, pass auth parameters def __init__(self, vdi_client): @@ -110,10 +110,10 @@ class InstanceRequestThread(QtCore.QThread): except (exceptions.WebGuardConnectionError, exceptions.WebGuardCookieError) as err: exc = exceptions.VDIServerResponseError( - err.message, code=exceptions.ErrorCodes.AUTH_ERROR) + str(err), code=exceptions.ErrorCodes.AUTH_ERROR) self.response_error.emit(exc.to_json()) except Exception as err: - message = getattr(err, 'message', unicode(err)) + message = getattr(err, 'message', str(err)) self.request_error.emit(message) @@ -185,7 +185,7 @@ class InstanceRequestWindow(QDialog): SESSION.rdp_token = vm_request.get('rdp_token') logger.debug('Got instance ip: %s', ip) self.ui.label_details.setText( - _(u'Connecting to instance {}...').format(ip)) + _('Connecting to instance {}...').format(ip)) self.start_connection_thread() def on_state_updated(self, vm_request_json): @@ -201,7 +201,7 @@ class InstanceRequestWindow(QDialog): state_details = REQUEST_PROCESS_MSG server_task_state = info.get('task_state') - if server_task_state in ServerTaskStates.keys(): + if server_task_state in list(ServerTaskStates.keys()): state_details = ACTION_PERFOMED_MSG.format( ServerTaskStates[server_task_state]) if state_details: @@ -215,7 +215,7 @@ class InstanceRequestWindow(QDialog): reason = _('Invalid credentials') if err.code in exceptions.ErrorCodes.reset_token_errors: - reason = err.message + reason = str(err) change_pwd = True elif err.code == exceptions.ErrorCodes.AUTH_ERROR: if SESSION.smartcard_used: @@ -226,10 +226,10 @@ class InstanceRequestWindow(QDialog): QMessageBox.Yes, QMessageBox.No) change_pwd = reply == QMessageBox.Yes else: - detailed_error(self, reason, err.message, + detailed_error(self, reason, str(err), detailed=get_log_msg()) else: - detailed_error(self, _('Connection error'), err.message, + detailed_error(self, _('Connection error'), str(err), detailed=get_log_msg()) if change_pwd: @@ -253,8 +253,7 @@ class InstanceRequestWindow(QDialog): def on_connection_error(self, error_msg): self.set_progress_animation(active=False) - detailed_error(self, _('Connection error'), error_msg, - detailed=get_log_msg()) + logger.error('Connection reset: %s', error_msg) self.connection_thread.wait() self.close() diff --git a/tionix_vdi_client/gui/resources_rc.py b/tionix_vdi_client/gui/resources_rc.py index 67c64bb..3c4c330 100644 --- a/tionix_vdi_client/gui/resources_rc.py +++ b/tionix_vdi_client/gui/resources_rc.py @@ -7,7 +7,7 @@ # # WARNING! All changes made in this file will be lost! -from PySide2 import QtCore +from PyQt5 import QtCore qt_resource_data = b"\ \x00\x00\x01\x8d\ diff --git a/tionix_vdi_client/gui/settings_window.py b/tionix_vdi_client/gui/settings_window.py index 38dce45..555abdd 100644 --- a/tionix_vdi_client/gui/settings_window.py +++ b/tionix_vdi_client/gui/settings_window.py @@ -3,9 +3,9 @@ import sys import logging -from PySide2 import QtCore -from PySide2.QtGui import QStandardItem, QStandardItemModel -from PySide2.QtWidgets import QDialog, QMessageBox +from PyQt5 import QtCore +from PyQt5.QtGui import QStandardItem, QStandardItemModel +from PyQt5.QtWidgets import QDialog, QMessageBox from tionix_vdi_client import Localization from tionix_vdi_client.gui.ui_settings import Ui_SettingsWindow @@ -147,7 +147,7 @@ class RDPSettingsWindow(SettingsWindow): # store devices and get by index later self._devices.append(device.DeviceID) child = QStandardItem( - "{} ({})".format(device.Name.encode("utf-8"), device.DeviceID)) + "{} ({})".format(device.Name.encode().decode("utf-8"), device.DeviceID)) devices_root.appendRow(child) child.setCheckable(True) if device.DeviceID in parsed_selected_devices: @@ -167,16 +167,16 @@ class RDPSettingsWindow(SettingsWindow): other_devices=False, use_multimon=False): content = [] conf = [ - u'domain:s:{}\r\n'.format(domain), - u'use multimon:i:{}\r\n'.format(int(use_multimon)), - u'usbdevicestoredirect:s:{}\r\n'.format(u';'.join(devices)), - u'devicestoredirect:s:{}\r\n'.format( - u'*' if other_devices else u''), - u'drivestoredirect:s:{}\r\n'.format(u';'.join(drives)) + 'domain:s:{}\r\n'.format(domain), + 'use multimon:i:{}\r\n'.format(int(use_multimon)), + 'usbdevicestoredirect:s:{}\r\n'.format(';'.join(devices)), + 'devicestoredirect:s:{}\r\n'.format( + '*' if other_devices else ''), + 'drivestoredirect:s:{}\r\n'.format(';'.join(drives)) ] updated_keys = [line.split(':')[0] for line in conf] for line in read_rdp_config(RDP_FILE_SAMPLE_PATH): - key = unicode(line.split(':')[0]) + key = str(line.split(':')[0]) if key in updated_keys: continue content.append(line) @@ -199,7 +199,7 @@ class RDPSettingsWindow(SettingsWindow): other_drives = ( other_drives_item.checkState() == QtCore.Qt.Checked) if other_drives: - selected_drives.append(u'DynamicDrives') + selected_drives.append('DynamicDrives') if all(drive in selected_drives for drive in self._drives) and other_drives: selected_drives = ['*'] diff --git a/tionix_vdi_client/gui/ui_main.py b/tionix_vdi_client/gui/ui_main.py index 64308cc..ace5749 100644 --- a/tionix_vdi_client/gui/ui_main.py +++ b/tionix_vdi_client/gui/ui_main.py @@ -8,7 +8,7 @@ # # WARNING! All changes made in this file will be lost! -from PySide2 import QtCore, QtGui, QtWidgets +from PyQt5 import QtCore, QtGui, QtWidgets class Ui_MainWindow(object): def setupUi(self, MainWindow): @@ -147,4 +147,4 @@ class Ui_MainWindow(object): self.button_exit.setText(QtWidgets.QApplication.translate("MainWindow", "Exit", None, -1)) self.label_version.setText(QtWidgets.QApplication.translate("MainWindow", "Version {version}", None, -1)) -import resources_rc +from . import resources_rc diff --git a/tionix_vdi_client/gui/ui_password.py b/tionix_vdi_client/gui/ui_password.py index aa8e7f1..68f91ed 100644 --- a/tionix_vdi_client/gui/ui_password.py +++ b/tionix_vdi_client/gui/ui_password.py @@ -8,7 +8,7 @@ # # WARNING! All changes made in this file will be lost! -from PySide2 import QtCore, QtGui, QtWidgets +from PyQt5 import QtCore, QtGui, QtWidgets class Ui_PasswordWindow(object): def setupUi(self, PasswordWindow): @@ -102,4 +102,4 @@ class Ui_PasswordWindow(object): self.button_cancel.setText(QtWidgets.QApplication.translate("PasswordWindow", "Cancel", None, -1)) self.button_save.setText(QtWidgets.QApplication.translate("PasswordWindow", "Save", None, -1)) -import resources_rc +from . import resources_rc diff --git a/tionix_vdi_client/gui/ui_pin.py b/tionix_vdi_client/gui/ui_pin.py index 122a038..7ba7cb7 100644 --- a/tionix_vdi_client/gui/ui_pin.py +++ b/tionix_vdi_client/gui/ui_pin.py @@ -8,7 +8,7 @@ # # WARNING! All changes made in this file will be lost! -from PySide2 import QtCore, QtGui, QtWidgets +from PyQt5 import QtCore, QtGui, QtWidgets class Ui_PinWindow(object): def setupUi(self, PinWindow): @@ -70,4 +70,4 @@ class Ui_PinWindow(object): self.button_cancel.setText(QtWidgets.QApplication.translate("PinWindow", "Cancel", None, -1)) self.button_ok.setText(QtWidgets.QApplication.translate("PinWindow", "OK", None, -1)) -import resources_rc +from . import resources_rc diff --git a/tionix_vdi_client/gui/ui_request.py b/tionix_vdi_client/gui/ui_request.py index ebaeab9..7da1860 100644 --- a/tionix_vdi_client/gui/ui_request.py +++ b/tionix_vdi_client/gui/ui_request.py @@ -8,7 +8,7 @@ # # WARNING! All changes made in this file will be lost! -from PySide2 import QtCore, QtGui, QtWidgets +from PyQt5 import QtCore, QtGui, QtWidgets class Ui_InstanceRequestWindow(object): def setupUi(self, InstanceRequestWindow): @@ -57,4 +57,4 @@ class Ui_InstanceRequestWindow(object): InstanceRequestWindow.setWindowTitle(QtWidgets.QApplication.translate("InstanceRequestWindow", "Request instance", None, -1)) self.button_cancel.setText(QtWidgets.QApplication.translate("InstanceRequestWindow", "Cancel", None, -1)) -import resources_rc +from . import resources_rc diff --git a/tionix_vdi_client/gui/ui_settings.py b/tionix_vdi_client/gui/ui_settings.py index b0181c5..b899222 100644 --- a/tionix_vdi_client/gui/ui_settings.py +++ b/tionix_vdi_client/gui/ui_settings.py @@ -8,7 +8,7 @@ # # WARNING! All changes made in this file will be lost! -from PySide2 import QtCore, QtGui, QtWidgets +from PyQt5 import QtCore, QtGui, QtWidgets class Ui_SettingsWindow(object): def setupUi(self, SettingsWindow): @@ -157,4 +157,4 @@ class Ui_SettingsWindow(object): self.button_cancel.setText(QtWidgets.QApplication.translate("SettingsWindow", "Cancel", None, -1)) self.button_save.setText(QtWidgets.QApplication.translate("SettingsWindow", "Save", None, -1)) -import resources_rc +from . import resources_rc diff --git a/tionix_vdi_client/helpers.py b/tionix_vdi_client/helpers.py index 21cfdf8..93c7c52 100644 --- a/tionix_vdi_client/helpers.py +++ b/tionix_vdi_client/helpers.py @@ -4,8 +4,8 @@ import logging import platform import re import sys -import urlparse -from StringIO import StringIO +import urllib.parse +from io import StringIO from contextlib import contextmanager from random import SystemRandom @@ -20,14 +20,14 @@ ALLOWED_SCHEMES = ('http://', 'https://') def get_win_info(): - return u'{system} {version}'.format( + return '{system} {version}'.format( system=platform.system(), version=platform.release()) def get_linux_info(): distname, version, _id = platform.linux_distribution() - return u'{distname} {version}'.format( + return '{distname} {version}'.format( distname=distname, version=version) @@ -36,8 +36,8 @@ def get_os_version(): """Get operating system version""" system = platform.system() info_getters = { - u'Linux': get_linux_info, - u'Windows': get_win_info, + 'Linux': get_linux_info, + 'Windows': get_win_info, } info_getter = info_getters.get(system, platform.platform) return info_getter() @@ -73,14 +73,14 @@ def get_vdi_api_url(url): if not any(url.startswith(scheme) for scheme in ALLOWED_SCHEMES): url = ALLOWED_SCHEMES[0] + url - scheme, netloc, _path, _query, _fragment = urlparse.urlsplit(url) + scheme, netloc, _path, _query, _fragment = urllib.parse.urlsplit(url) netloc_parsed = netloc.split(':') if len(netloc_parsed) == 1: netloc_parsed.append(API_DEFAULT_PORT) netloc = ':'.join(netloc_parsed) - vdi_api_url = urlparse.urlunsplit((scheme, netloc, '', '', '')) + vdi_api_url = urllib.parse.urlunsplit((scheme, netloc, '', '', '')) return vdi_api_url diff --git a/tionix_vdi_client/i18n.py b/tionix_vdi_client/i18n.py index c4e3fe2..8a86a09 100644 --- a/tionix_vdi_client/i18n.py +++ b/tionix_vdi_client/i18n.py @@ -25,8 +25,8 @@ gettext.textdomain(APP_NAME) gettext.bind_textdomain_codeset(APP_NAME, "UTF-8") language = gettext.translation(APP_NAME, mo_location, languages=languages, fallback=True) -ugettext = language.ugettext -ungettext = language.ungettext +ugettext = language.gettext +ungettext = language.ngettext def switch_language(lang): @@ -39,5 +39,5 @@ def switch_language(lang): global ugettext, ungettext t = gettext.translation(APP_NAME, mo_location, languages=[lang], fallback=True) - ugettext = t.ugettext - ungettext = t.ungettext + ugettext = t.gettext + ungettext = t.ngettext diff --git a/tionix_vdi_client/identity.py b/tionix_vdi_client/identity.py index 76da9cb..1a06296 100644 --- a/tionix_vdi_client/identity.py +++ b/tionix_vdi_client/identity.py @@ -29,17 +29,17 @@ def request_token(identity_url, certpath, keypath): response = requests.get( url, cert=(certpath, keypath), verify=False, headers=headers) except requests.RequestException as err: - logger.error(u'Token request failed: %s', err) + logger.error('Token request failed: %s', err) errors.append(base_url) continue if not response.ok: - logger.error(u'Token request failed: %s', response.text) + logger.error('Token request failed: %s', response.text) errors.append(base_url) continue return response.headers.get('X-Subject-Token') raise exceptions.ApplicationConnectionError( - u'Failed to authenticate on: {}'.format(u', '.join(errors))) + 'Failed to authenticate on: {}'.format(', '.join(errors))) def _parse_issuer_dn(cert): diff --git a/tionix_vdi_client/log.py b/tionix_vdi_client/log.py index 2fcbbd2..7ac12a0 100644 --- a/tionix_vdi_client/log.py +++ b/tionix_vdi_client/log.py @@ -14,13 +14,13 @@ class RedactingFilter(logging.Filter): record.msg = self.redact(record.msg) if isinstance(record.args, dict): record.args = {key: self.redact(value) for key, value in - record.args.items()} + list(record.args.items())} else: record.args = tuple(self.redact(arg) for arg in record.args) return True def redact(self, msg): - msg = msg if isinstance(msg, basestring) else unicode(msg) + msg = msg if isinstance(msg, str) else str(msg) for expr in self._exprs: if isinstance(expr, tuple): diff --git a/tionix_vdi_client/main.py b/tionix_vdi_client/main.py index 46c8d90..e051cbe 100644 --- a/tionix_vdi_client/main.py +++ b/tionix_vdi_client/main.py @@ -16,9 +16,9 @@ logger = logging.getLogger('tionix_vdi_client') # noinspection PyTypeChecker def create_parser(): - utf8 = partial(unicode, encoding='utf-8') + utf8 = partial(str, encoding='utf-8') parser = argparse.ArgumentParser( - description=u'TIONIX.VDIclient utility', + description='TIONIX.VDIclient utility', conflict_handler='resolve') parser.add_argument( '--version', action='version', version=MODULE_VERSION) diff --git a/tionix_vdi_client/rdp.py b/tionix_vdi_client/rdp.py index 3faac41..29f2d3a 100644 --- a/tionix_vdi_client/rdp.py +++ b/tionix_vdi_client/rdp.py @@ -27,7 +27,7 @@ def read_rdp_config(filepath): except ValueError: continue except IOError: - logger.error(u"Couldn't read the rdp connect sample config file.") + logger.error("Couldn't read the rdp connect sample config file.") else: break return rdp_file_content @@ -36,8 +36,8 @@ def read_rdp_config(filepath): def save_rdp_config(filepath, content): with codecs.open(filepath, "w", encoding='utf-16le') as fh: for line in content: - if not line.endswith(u'\r\n'): - line += u'\r\n' + if not line.endswith('\r\n'): + line += '\r\n' fh.write(line) @@ -85,13 +85,13 @@ def _generate_win_rdp_config(host, domain, user, password, ignore_domain): if ignore_domain or conf['domain']: user_name = user else: - user_name = u'{0}\\{1}'.format(domain, user) + user_name = '{0}\\{1}'.format(domain, user) password_encrypted = binascii.hexlify(crypt_protect_data(password)).upper() rdp_file_content.extend([ - u"full address:s:{host}\r\n".format(host=host), - u"UserName:s:{user_name}\r\n".format(user_name=user_name), - u"password 51:b:{password_encrypted}\r\n".format( + "full address:s:{host}\r\n".format(host=host), + "UserName:s:{user_name}\r\n".format(user_name=user_name), + "password 51:b:{password_encrypted}\r\n".format( password_encrypted=password_encrypted) ]) @@ -137,7 +137,7 @@ def rdp_connect(user, password, domain_name, vm_ip, rdp_token, 'domain': domain_name } - logger.info(u"Running connection: user=%s, ip=%s", user, vm_ip) + logger.info("Running connection: user=%s, ip=%s", user, vm_ip) if sys.platform == "win32": # on Windows platform some special characters in password can't be @@ -168,11 +168,11 @@ def rdp_connect(user, password, domain_name, vm_ip, rdp_token, raise exceptions.ApplicationLogicException(bad_cmd_str) escaped_params.update({'password': '***'}) - logger.debug(u'Running command: %s', + logger.debug('Running command: %s', cmd_template.format(**escaped_params)) p = subprocess.Popen( - cmd.encode(sys.getfilesystemencoding()), + cmd.encode().decode(sys.getfilesystemencoding()), shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) @@ -181,17 +181,17 @@ def rdp_connect(user, password, domain_name, vm_ip, rdp_token, stop_event.wait(0.5) if stop_event.is_set(): p.kill() - logger.debug(u'RDP connection canceled') + logger.debug('RDP connection canceled') return out, err = p.communicate() if p.returncode: - message_err = err.decode(sys.stdout.encoding or 'utf-8').strip() - message_out = out.decode(sys.stdout.encoding or 'utf-8').strip() + message_err = err.encode().decode(sys.stdout.encoding or 'utf-8').strip() + message_out = out.encode().decode(sys.stdout.encoding or 'utf-8').strip() message = message_err or message_out - logger.error(u"Remote Access Client sent a message: %s", message) + logger.error("Remote Access Client sent a message: %s", message) if not silent: - msg = _(u"Remote Access Client sent a message: {}").format(message) + msg = _("Remote Access Client sent a message: {}").format(message) err = exceptions.ApplicationLogicException(msg) err.exit_code = p.returncode raise err diff --git a/tionix_vdi_client/settings.py b/tionix_vdi_client/settings.py index df76819..e544ed9 100644 --- a/tionix_vdi_client/settings.py +++ b/tionix_vdi_client/settings.py @@ -1,7 +1,7 @@ # coding: utf-8 # pylint: disable=no-name-in-module -import ConfigParser +import configparser import base64 import binascii import codecs @@ -18,7 +18,7 @@ from tionix_vdi_client import BASE_DIR as ORIGINAL_BASE_DIR, Localization FS_ENCODING = sys.getfilesystemencoding() APP_DIR_NAME = '.tionix-vdi-client' -APP_DIR_PATH = os.path.join(os.path.expanduser('~'), APP_DIR_NAME).decode( +APP_DIR_PATH = os.path.join(os.path.expanduser('~'), APP_DIR_NAME).encode().decode( FS_ENCODING) RDP_FILE_SAMPLE_NAME = 'rdp.conf.sample' RDP_FILE_SAMPLE_PATH = os.path.join(APP_DIR_PATH, RDP_FILE_SAMPLE_NAME) @@ -29,7 +29,7 @@ if getattr(sys, 'frozen', False): else: BASE_DIR = ORIGINAL_BASE_DIR -BASE_DIR = BASE_DIR.decode(FS_ENCODING) +BASE_DIR = BASE_DIR.encode().decode(FS_ENCODING) _settings = None # Settings cache if reread settings is not required @@ -51,10 +51,10 @@ LOGGING = { 'disable_existing_loggers': True, 'formatters': { 'default': { - 'format': u'%(asctime)s.%(msecs)03d %(process)d %(threadName)s ' - u'%(levelname)s %(name)s %(filename)s:%(lineno)d ' - u'[-] %(message)s', - 'datefmt': u'%Y-%m-%d %H:%M:%S', + 'format': '%(asctime)s.%(msecs)03d %(process)d %(threadName)s ' + '%(levelname)s %(name)s %(filename)s:%(lineno)d ' + '[-] %(message)s', + 'datefmt': '%Y-%m-%d %H:%M:%S', } }, 'handlers': { @@ -114,9 +114,9 @@ logger = logging.getLogger(__name__) def get_default_connect_param(): if sys.platform == "win32": - conn_cmd = u'mstsc {rdp_config}' + conn_cmd = 'mstsc {rdp_config}' else: - conn_cmd = u'xfreerdp /u:{user} /p:{password} /v:{ip}' + conn_cmd = 'xfreerdp /u:{user} /p:{password} /v:{ip} /cert:tofu' return conn_cmd @@ -126,10 +126,10 @@ def get_default_locale(): def find_pycs11_lib(): - result = u'' - filename = u'libisbc_pkcs11_main.so' + result = '' + filename = 'libisbc_pkcs11_main.so' paths = ( - os.path.join(os.getcwd().decode(FS_ENCODING), filename), + os.path.join(os.getcwd().encode().decode(FS_ENCODING), filename), os.path.join(APP_DIR_PATH, filename) ) for path in paths: @@ -141,18 +141,18 @@ def find_pycs11_lib(): # noinspection PyTypeChecker def validate_log_level(v, def_v): - if isinstance(v, (str, unicode)): + if isinstance(v, str): v = v.upper() - return def_v if isinstance(logging.getLevelName(v), basestring) else v + return def_v if isinstance(logging.getLevelName(v), str) else v # noinspection PyTypeChecker def validate_bool(val, default_value=False): - if isinstance(val, basestring): + if isinstance(val, str): val = val.lower() - if val in (True, u'true', u'yes', u'y', u'1', 1): + if val in (True, 'true', 'yes', 'y', '1', 1): value = True - elif val in (False, u'false', u'no', u'n', u'0', 0): + elif val in (False, 'false', 'no', 'n', '0', 0): value = False else: value = default_value @@ -169,7 +169,7 @@ def validate_use_smartcard(val, default_value): # noinspection PyTypeChecker def validate_int(val, default_value): - if isinstance(val, basestring): + if isinstance(val, str): try: val = int(val) except ValueError: @@ -189,14 +189,14 @@ def validate_positive_int(val, default_value): # noinspection PyTypeChecker def validate_list(val, default_value): if not isinstance(val, (list, tuple)): - if not isinstance(val, basestring): + if not isinstance(val, str): val = default_value val = val.split(',') val = [v.strip() for v in val if v.strip()] return val -def validate_str(val, def_v=u''): +def validate_str(val, def_v=''): val = val.strip() if not val: val = def_v @@ -209,8 +209,8 @@ def validate_max_length(val, default): if not val: return default elif len(val) > MAX_LENGTH_CONFIG_MSG: - logger.debug(u"The allowed message length has been exceed 250 " - u"characters. The message will be cut off.") + logger.debug("The allowed message length has been exceed 250 " + "characters. The message will be cut off.") return val[:MAX_LENGTH_CONFIG_MSG] @@ -228,18 +228,18 @@ def validate_log_file_location(val, def_value): def validate_language(val, def_value): result = def_value - available_languages = Localization.locales_dict.keys() + available_languages = list(Localization.locales_dict.keys()) # noinspection PyTypeChecker - if isinstance(val, basestring): + if isinstance(val, str): val = val.lower().strip() if val in available_languages: result = val else: logger.warning( - u'Language "%s" is not supported. Available languages are:' - u' "%s". Your system language is "%s", so it is set as the ' - u'default language.', - val, u', '.join(available_languages), def_value) + 'Language "%s" is not supported. Available languages are:' + ' "%s". Your system language is "%s", so it is set as the ' + 'default language.', + val, ', '.join(available_languages), def_value) return result @@ -260,11 +260,11 @@ def get_log_file_path(dir_path=None): dir_path = dir_path if dir_path else APP_DIR_PATH dir_path = os.path.abspath(dir_path) - current_user = getpass.getuser().decode(FS_ENCODING) + current_user = getpass.getuser().encode().decode(FS_ENCODING) available_names = ( - u'{0}.{1}'.format(LOG_FILE_NAME, LOG_FILE_EXT), - u'{0}-{1}.{2}'.format(LOG_FILE_NAME, current_user, LOG_FILE_EXT), + '{0}.{1}'.format(LOG_FILE_NAME, LOG_FILE_EXT), + '{0}-{1}.{2}'.format(LOG_FILE_NAME, current_user, LOG_FILE_EXT), ) for name in available_names: @@ -275,12 +275,12 @@ def get_log_file_path(dir_path=None): else: _id, path = tempfile.mkstemp( prefix=LOG_FILE_NAME + '-', suffix='.' + LOG_FILE_EXT) - path.decode(FS_ENCODING) + path.encode().decode(FS_ENCODING) return path def update_nested_dict(initial_dict, update_dict): - for key, val in update_dict.items(): + for key, val in list(update_dict.items()): if isinstance(val, dict): initial_dict[key] = update_nested_dict( initial_dict.get(key, {}), val) @@ -293,7 +293,7 @@ class ConfigReadError(Exception): pass -class UnicodeConfigParser(ConfigParser.ConfigParser): +class UnicodeConfigParser(configparser.ConfigParser): FILE_READ_ENCODINGS = ('utf-8-sig', 'cp1251') def write(self, fp): @@ -304,26 +304,26 @@ class UnicodeConfigParser(ConfigParser.ConfigParser): """ linesep = os.linesep if self._defaults: - fp.write(u"[{section}]".format(section=ConfigParser.DEFAULTSECT)) + fp.write("[{section}]".format(section=configparser.DEFAULTSECT)) fp.write(linesep) - for (key, value) in self._defaults.items(): + for (key, value) in list(self._defaults.items()): if isinstance(value, str): - value = value.decode('utf-8') - value = unicode(value).replace('\n', '\n\t') - fp.write(u"{key} = {value}".format(key=key, value=value)) + value = value.encode().decode('utf-8') + value = str(value).replace('\n', '\n\t') + fp.write("{key} = {value}".format(key=key, value=value)) fp.write(linesep) fp.write(linesep) for section in self._sections: - fp.write(u"[{section}]".format(section=section)) + fp.write("[{section}]".format(section=section)) fp.write(linesep) - for (key, value) in self._sections[section].items(): + for (key, value) in list(self._sections[section].items()): if key == "__name__": continue if (value is not None) or (self._optcre == self.OPTCRE): if isinstance(value, str): - value = value.decode('utf-8') - value = unicode(value).replace('\n', '\n\t') - fp.write(u"{key} = {value}".format(key=key, value=value)) + value = value.encode().decode('utf-8') + value = str(value).replace('\n', '\n\t') + fp.write("{key} = {value}".format(key=key, value=value)) fp.write(linesep) fp.write(linesep) @@ -331,7 +331,7 @@ class UnicodeConfigParser(ConfigParser.ConfigParser): val = kwargs.pop('default', None) try: val = self.get(*args, **kwargs) - except ConfigParser.NoOptionError: + except configparser.NoOptionError: pass return val @@ -342,23 +342,22 @@ class UnicodeConfigParser(ConfigParser.ConfigParser): """ for encoding in self.FILE_READ_ENCODINGS: try: - with codecs.open(filepath, encoding=encoding) as fp: - self.readfp(fp) + self.read(filepath, encoding=encoding) except ValueError: continue except IOError as exc: logger.error( - u'Error while reading file %s: %s', filepath, exc) + 'Error while reading file %s: %s', filepath, exc) raise ConfigReadError() - except (ConfigParser.MissingSectionHeaderError, - ConfigParser.ParsingError) as exc: + except (configparser.MissingSectionHeaderError, + configparser.ParsingError) as exc: logger.error( - u'Error while reading file %s: %s', filepath, exc) + 'Error while reading file %s: %s', filepath, exc) raise ConfigReadError() else: break else: - logger.error(u'Failed to detect encoding of file %s', filepath) + logger.error('Failed to detect encoding of file %s', filepath) raise ConfigReadError() def write_file(self, filepath): @@ -387,7 +386,7 @@ class Settings(dict): if name in self._attributes or name in self._extra_attributes: return self.get(name, '') else: - msg = u"'{0}' object has no attribute '{1}'".format( + msg = "'{0}' object has no attribute '{1}'".format( type(self).__name__, name) raise AttributeError(msg) @@ -395,7 +394,7 @@ class Settings(dict): if item in self._attributes or item in self._extra_attributes: self[item] = value else: - msg = u"'{0}' object has no attribute '{1}'".format( + msg = "'{0}' object has no attribute '{1}'".format( type(self).__name__, item) raise AttributeError(msg) @@ -420,7 +419,7 @@ class SessionSettings(Settings): @property def _session_file_name(self): - return os.path.join(APP_DIR_PATH, u'tnx_client_session') + return os.path.join(APP_DIR_PATH, 'tnx_client_session') def read(self): resave = False @@ -432,37 +431,37 @@ class SessionSettings(Settings): for key, value in config.items(self._session_section_name): if key == self.PASSWORD and not CONF.store_password and value: resave = True - value = u"" + value = "" elif key in self._encode_keys: try: - value = base64.decodestring(value).decode('utf-8') + value = base64.decodebytes(value.encode()).decode('utf-8') except binascii.Error: - logger.error(u"Failed to decode {}. Value is " - u"considered as empty.".format(key)) - value = u"" + logger.error("Failed to decode {}. Value is " + "considered as empty.".format(key)) + value = "" setattr(self, key, value) if resave: self.save() def save(self): config = self._config_class() - for key, value in self.items(): + for key, value in list(self.items()): if key not in self._attributes: continue if value is None: value = '' if key in self._encode_keys: if not isinstance(value, str): - value = value.encode('utf-8') - value = base64.encodestring(value).decode('utf-8') + value = value.encode().decode('utf-8') + value = base64.encodebytes(value.encode()).decode('utf-8') if key == self.PASSWORD and not CONF.store_password: value = '' config.set(self._session_section_name, key, value) try: config.write_file(self._session_file_name) except IOError as exc: - logger.warning(u'Error while saving session to ' - u'file: %s', exc) + logger.warning('Error while saving session to ' + 'file: %s', exc) def clear(self): for attr in self._attributes: @@ -546,34 +545,34 @@ class ConfigSettings(Settings): ) default = { - Settings.CLOUD: u'', + Settings.CLOUD: '', Settings.IGNORE_DOMAIN: False, - Settings.SECONDARY_CLOUD: u'', + Settings.SECONDARY_CLOUD: '', Settings.CONNECT: get_default_connect_param(), - Settings.DOMAIN_NAME: u'default', - LOG_LEVEL: u'INFO', + Settings.DOMAIN_NAME: 'default', + LOG_LEVEL: 'INFO', STORE_SESSION: True, STORE_PASSWORD: False, - LOG_FILE_LOCATION: u'', + LOG_FILE_LOCATION: '', Settings.LANGUAGE: get_default_locale(), PROJECT: True, SHOW_SETTINGS: True, PYKCS11_LIB: find_pycs11_lib(), - SMART_OID: u'', - IKECFG: u'', - ADDITIONAL_CLOUDS: u'', + SMART_OID: '', + IKECFG: '', + ADDITIONAL_CLOUDS: '', RETRIES: 2, TIMEOUT: 15, USE_SMARTCARD: True, - SUPPORT_MESSAGE_RU: u'Обратитесь к системному администратору.', - SUPPORT_MESSAGE_EN: u'Please contact system administrator.', + SUPPORT_MESSAGE_RU: 'Обратитесь к системному администратору.', + SUPPORT_MESSAGE_EN: 'Please contact system administrator.', PASSWORD_GENERATION: False, GET_VM_TIMEOUT: 5, WEB_GUARD: False, USE_CERT: False, - CERT: u'', - KEY: u'', - IDENTITY_URL: u'', + CERT: '', + KEY: '', + IDENTITY_URL: '', MINIMIZE_TO_TRAY: False, SINGLE_LAUNCH: False, SILENT: False, @@ -634,7 +633,7 @@ class ConfigSettings(Settings): self._default_config_name) self.save() except IOError: - logger.info(u"Couldn't create config file, using defaults!") + logger.info("Couldn't create config file, using defaults!") @classmethod def validator(cls, setting_param): @@ -670,19 +669,19 @@ class ConfigSettings(Settings): Save settings to file """ config = self._config_class() - if self.items(): + if list(self.items()): for key in self.ordered_file_variables: value = getattr(self, key) if key in self.hidden_defaults and value == self.default[key]: continue if isinstance(value, (list, tuple)): value = ','.join(value) - config.set(self._config_section_name, key, value) + config.set(self._config_section_name, key, str(value)) try: config.write_file(self._default_config_name) except IOError as exc: - logger.warning(u'Error while saving settings to ' - u'file: %s', exc) + logger.warning('Error while saving settings to ' + 'file: %s', exc) def update_logging_level(): @@ -716,10 +715,10 @@ def get_logging_config(level=None, log_file_dir_path=None): if previous_log_path: if previous_log_path != log_file_path: - logger.info(u'Log file location changed from "%s" to "%s"', + logger.info('Log file location changed from "%s" to "%s"', previous_log_path, log_file_path) else: - logger.info(u'Log file path: %s', log_file_path) + logger.info('Log file path: %s', log_file_path) custom_config = { 'handlers': { @@ -769,5 +768,5 @@ if sys.platform == "win32": shutil.copy(os.path.join(BASE_DIR, RDP_FILE_SAMPLE_NAME), RDP_FILE_SAMPLE_PATH) except IOError as err: - logger.error(u"Couldn't create rdp connect sample config file." - u" %s", unicode(err)) + logger.error("Couldn't create rdp connect sample config file." + " %s", str(err)) diff --git a/tionix_vdi_client/smart_card/__init__.py b/tionix_vdi_client/smart_card/__init__.py index bc03338..6f97c73 100644 --- a/tionix_vdi_client/smart_card/__init__.py +++ b/tionix_vdi_client/smart_card/__init__.py @@ -13,7 +13,7 @@ if sys.platform != 'win32' and CONF.use_smartcard: from tionix_vdi_client.smart_card import api except ImportError as err: logger.error( - u"Failed to import tionix_vdi_client.smart_card.api: %s", err, + "Failed to import tionix_vdi_client.smart_card.api: %s", err, exc_info=TRACEBACK_ENABLED) api = None else: diff --git a/tionix_vdi_client/smart_card/api.py b/tionix_vdi_client/smart_card/api.py index c42ae57..ef0477c 100644 --- a/tionix_vdi_client/smart_card/api.py +++ b/tionix_vdi_client/smart_card/api.py @@ -27,22 +27,22 @@ Certificate = namedtuple( ['subject_email', 'issuer_email', 'start_date', 'end_date', 'oids']) SMARTCARD_ERROR_MSG = ( - u"Couldn't connect to smart card. " - u"Login/Pass will be searched in config files. " - u"Error: %s") + "Couldn't connect to smart card. " + "Login/Pass will be searched in config files. " + "Error: %s") def _retrieve_username(session, oid=None): user_certificate = _find_user_certificate(session, oid) user_email = user_certificate.subject_email - return user_email.split('@')[0].decode('utf-8') + return user_email.split('@')[0].encode().decode('utf-8') def _get_objects(session, template): try: vdi_files = session.findObjects(template) except PyKCS11Error as err: - logger.warning(u'Error while searching files on smart card: %s.', err) + logger.warning('Error while searching files on smart card: %s.', err) vdi_files = [] return vdi_files @@ -51,12 +51,12 @@ def _delete_object(session, obj): try: session.destroyObject(obj) except PyKCS11Error as err: - logger.warning(u'Error while deleting file on smart card: %s', err) + logger.warning('Error while deleting file on smart card: %s', err) def _get_password_template(login): - if isinstance(login, unicode): - login = login.encode('utf-8') + if isinstance(login, str): + login = login.encode().decode('utf-8') template = [ (CKA_CLASS, CKO_DATA), (CKA_TOKEN, CK_TRUE), @@ -69,7 +69,7 @@ def _create_object(session, template): try: result = session.createObject(template) except PyKCS11Error as err: - logger.warning(u'Error while creating file on smart card: %s.', err) + logger.warning('Error while creating file on smart card: %s.', err) result = None return result @@ -90,7 +90,7 @@ def _get_oids_from_x509cert(x509_cert): if isinstance(v, ObjectIdentifier): oids.append(v.prettyPrint()) except Exception as err: - logger.warning(u'Failed to parse certificate OIDs: %s', err) + logger.warning('Failed to parse certificate OIDs: %s', err) return oids @@ -107,7 +107,7 @@ def _get_certificates(session): x509_cert = load_certificate( FILETYPE_ASN1, str(bytearray(attributes[CKA_VALUE]))) except Exception as err: - logger.debug(u'Failed to load certificate: %s', err) + logger.debug('Failed to load certificate: %s', err) continue subject_email = x509_cert.get_subject().emailAddress or '' @@ -133,33 +133,33 @@ def _find_user_certificate(session, oid=None): if not certificates: raise exceptions.CertificateNotFound() logger.debug( - u'Found following certificates:\n%s', - u'\n'.join( - u'{{subject_email={0}, issuer_email={1}, ' - u'start_date={2}, end_date={3}, OIDs={4}}}'.format( + 'Found following certificates:\n%s', + '\n'.join( + '{{subject_email={0}, issuer_email={1}, ' + 'start_date={2}, end_date={3}, OIDs={4}}}'.format( c.subject_email, c.issuer_email, c.start_date, c.end_date, c.oids) for c in certificates)) now = datetime.utcnow() filtered_certs = [] for k, cert in enumerate(certificates, start=1): - logger.debug(u'Processing certificate #%s', k) + logger.debug('Processing certificate #%s', k) if not cert.subject_email: - logger.debug(u'Certificate does not contain subject email') + logger.debug('Certificate does not contain subject email') continue if cert.subject_email == cert.issuer_email: - logger.debug(u"Skipping self-signed certificate") + logger.debug("Skipping self-signed certificate") continue if oid and oid not in cert.oids: logger.debug( - u'Certificate does not contain required EKU OID %s', oid) + 'Certificate does not contain required EKU OID %s', oid) continue if cert.end_date < now: - logger.debug(u'Certificate expired') + logger.debug('Certificate expired') continue filtered_certs.append(cert) @@ -181,20 +181,20 @@ def _retrieve_password(session, username): vdi_file = vdi_files[0] try: attributes = session.getAttributeValue(vdi_file, [CKA_VALUE]) - result_password = str(bytearray(attributes[0])).decode('utf-8') + result_password = str(bytearray(attributes[0])).encode().decode('utf-8') except PyKCS11Error as err: logger.warning( - u'Error while reading vdi file: %s.', err) + 'Error while reading vdi file: %s.', err) return result_password def get_session(): if CONF.PYKCS11LIB and "PYKCS11LIB" not in os.environ: if os.path.exists(CONF.PYKCS11LIB): - os.environ["PYKCS11LIB"] = CONF.PYKCS11LIB.encode('utf-8') + os.environ["PYKCS11LIB"] = CONF.PYKCS11LIB.encode.decode('utf-8') else: logger.warning( - u"PYKCS11LIB file is not found: %s", CONF.PYKCS11LIB) + "PYKCS11LIB file is not found: %s", CONF.PYKCS11LIB) raise exceptions.SmartCardNotConfigured() pkcs11 = PyKCS11Lib() @@ -203,26 +203,26 @@ def get_session(): pkcs11.load() except PyKCS11Error as err: logger.warning( - u'Couldn"t load pkcs11 lib: %s. Please check "PYKCS11LIB" option' - u' in the configuration file', err) + 'Couldn"t load pkcs11 lib: %s. Please check "PYKCS11LIB" option' + ' in the configuration file', err) raise exceptions.SmartCardNotConfigured() - logger.debug(u'PKCS11 lib initialized.') + logger.debug('PKCS11 lib initialized.') slots = pkcs11.getSlotList() if not slots: - logger.warning(u'No card readers found. Login/Pass will be ' - u'searched in config files.') + logger.warning('No card readers found. Login/Pass will be ' + 'searched in config files.') raise exceptions.SmartCardNotConfigured() slot = slots[0] - logger.info(u'Found card reader (slot): %s', + logger.info('Found card reader (slot): %s', pkcs11.getSlotInfo(slot).slotDescription) try: token_info = pkcs11.getTokenInfo(slot) except PyKCS11Error as err: - logger.error(u'Failed to obtain token info: %s.', err) + logger.error('Failed to obtain token info: %s.', err) raise exceptions.SmartCardNotConfigured() try: @@ -244,7 +244,7 @@ def get_pin_attempts(): with PCSCClient() as pcsc: attempts = pcsc.get_pin_attempts() except PCSCError as err: - logger.warning(u'Failed to get pin attempts: %s', err) + logger.warning('Failed to get pin attempts: %s', err) return attempts @@ -253,7 +253,7 @@ def get_session_data(session, pin): session.login(pin) except PyKCS11Error as err: if err.value == CKR_PIN_INCORRECT: - error_msg = _(u"You've entered an incorrect PIN.") + error_msg = _("You've entered an incorrect PIN.") logger.error(error_msg) raise exceptions.IncorrectPin(error_msg) @@ -273,7 +273,7 @@ def update_password(session, username, new_password): _delete_object(session, vdi_file) store_template = template + [ - (CKA_VALUE, new_password.encode('utf-8')), + (CKA_VALUE, new_password.encode().decode('utf-8')), (CKA_PRIVATE, CK_TRUE) ] return _create_object(session, store_template) diff --git a/tionix_vdi_client/smart_card/pcsc.py b/tionix_vdi_client/smart_card/pcsc.py index 234161c..2b580ae 100644 --- a/tionix_vdi_client/smart_card/pcsc.py +++ b/tionix_vdi_client/smart_card/pcsc.py @@ -129,11 +129,11 @@ class PCSCClient(object): def connection(self): if not self._connection: if readers is None: - raise PCSCError(u'pyscard package is not installed.') + raise PCSCError('pyscard package is not installed.') # noinspection PyCallingNonCallable scard_readers = readers() if not scard_readers: - raise PCSCError(u'Smart card readers not found.') + raise PCSCError('Smart card readers not found.') reader = scard_readers[0] self._connection = reader.createConnection() self._connection.connect() @@ -141,12 +141,12 @@ class PCSCClient(object): def transmit(self, command): command_bytes = command.to_bytes() - logger.debug(u'Sending APDU command: {0}'.format( - u' '.join(u'{:02X}'.format(i) for i in command_bytes))) - print + logger.debug('Sending APDU command: {0}'.format( + ' '.join('{:02X}'.format(i) for i in command_bytes))) + print() data, sw1, sw2 = self.connection.transmit(command_bytes) logger.debug( - u'Response status words: {0:02X} {1:02X}'.format(sw1, sw2)) + 'Response status words: {0:02X} {1:02X}'.format(sw1, sw2)) return data, sw1, sw2 def select_file(self, file_path): @@ -155,7 +155,7 @@ class PCSCClient(object): select_cmd.p1 = SELECT_P1_ROOT if i == 0 else SELECT_P1_CHILD _data, sw1, _sw2 = self.transmit(select_cmd) if sw1 != SELECT_SW1_SUCCESS: - raise PCSCError(u'SELECT FILE failed.') + raise PCSCError('SELECT FILE failed.') def verify_pin(self, pin=None): self.select_file(self._auth_path) @@ -171,7 +171,7 @@ class PCSCClient(object): elif sw1 == 0x63 and 0xC1 <= sw2 <= 0xCF: attempts = sw2 - 0xC0 else: - raise PCSCError(u'Failed to get PIN attempts.') + raise PCSCError('Failed to get PIN attempts.') return attempts def disconnect(self): diff --git a/tionix_vdi_client/tests/functional/func_test_vdi_client_check_config_files.py b/tionix_vdi_client/tests/functional/func_test_vdi_client_check_config_files.py index 81ff925..df25ab2 100644 --- a/tionix_vdi_client/tests/functional/func_test_vdi_client_check_config_files.py +++ b/tionix_vdi_client/tests/functional/func_test_vdi_client_check_config_files.py @@ -3,24 +3,24 @@ import unittest # noinspection PyPep8Naming -import ConfigParser as configparser +import configparser as configparser from tionix_vdi_client.i18n import ugettext as _, ungettext from tionix_vdi_client.settings import ConfigSettings, SessionSettings class ConfigFilesTest(unittest.TestCase): - msg_save_failed = _(u"Failed save to {0} file. " - u"Please check write permissions of {1}.") - config_transl = _(u'config') - session_transl = _(u'session') + msg_save_failed = _("Failed save to {0} file. " + "Please check write permissions of {1}.") + config_transl = _('config') + session_transl = _('session') @staticmethod def find_empty_parameters(settings): # type: (ConfigSettings or SessionSettings) -> list return [ key for key in settings.required_attributes if - key not in settings or settings[key] in {None, u''} + key not in settings or settings[key] in {None, ''} ] def test_config_file_read_save(self): @@ -35,7 +35,7 @@ class ConfigFilesTest(unittest.TestCase): conf_parser.read(conf_settings_location) except (configparser.ParsingError, configparser.MissingSectionHeaderError): - self.fail(_(u"Failed to parse config file.")) + self.fail(_("Failed to parse config file.")) test_cloud = 'test.loc' @@ -51,7 +51,7 @@ class ConfigFilesTest(unittest.TestCase): msg_failed_save_config = self.msg_save_failed.format( self.config_transl, conf_settings_location) - self.assertNotEquals(orig_cloud, config_file.cloud, + self.assertNotEqual(orig_cloud, config_file.cloud, msg=_(msg_failed_save_config)) config_file.cloud = orig_cloud @@ -59,7 +59,7 @@ class ConfigFilesTest(unittest.TestCase): config_file.read(reread=True) - self.assertEquals(orig_cloud, config_file.cloud, + self.assertEqual(orig_cloud, config_file.cloud, msg=_(msg_failed_save_config)) def test_session_file_read_save(self): @@ -79,7 +79,7 @@ class ConfigFilesTest(unittest.TestCase): conf_parser.read(session_file_location) except (configparser.ParsingError, configparser.MissingSectionHeaderError): - self.fail(_(u"Failed to parse session file.")) + self.fail(_("Failed to parse session file.")) sessions_settings.read() orig_user = sessions_settings.user @@ -110,11 +110,11 @@ class ConfigFilesTest(unittest.TestCase): missing_params = self.find_empty_parameters(config) if missing_params: self.fail(ungettext( - u'Parameter "{}" is missing in config ' - u'file.', - u'Parameters "{}" are missing in config ' - u'file.', - len(missing_params)).format(u", ".join(missing_params))) + 'Parameter "{}" is missing in config ' + 'file.', + 'Parameters "{}" are missing in config ' + 'file.', + len(missing_params)).format(", ".join(missing_params))) def test_session_file_has_required_values(self): """Checks that all required values are present in session file. @@ -126,8 +126,8 @@ class ConfigFilesTest(unittest.TestCase): missing_params = self.find_empty_parameters(session_settings) if missing_params: self.fail(ungettext( - u'Parameter "{}" is missing in session ' - u'file.', - u'Parameters "{}" are missing in session ' - u'file.', - len(missing_params)).format(u", ".join(missing_params))) + 'Parameter "{}" is missing in session ' + 'file.', + 'Parameters "{}" are missing in session ' + 'file.', + len(missing_params)).format(", ".join(missing_params))) diff --git a/tionix_vdi_client/tests/functional/func_test_vdi_client_check_localization.py b/tionix_vdi_client/tests/functional/func_test_vdi_client_check_localization.py index 06714df..fce2c58 100644 --- a/tionix_vdi_client/tests/functional/func_test_vdi_client_check_localization.py +++ b/tionix_vdi_client/tests/functional/func_test_vdi_client_check_localization.py @@ -16,5 +16,5 @@ class LocalizationFilesTests(unittest.TestCase): locale_file_path = path.join(LOCALE_DIR, 'ru_RU/LC_MESSAGES/%s.mo' % APP_NAME) self.assertTrue(path.exists(locale_file_path), - _(u"Localization file is missing. It should be " - u"located at {0}.").format(locale_file_path)) + _("Localization file is missing. It should be " + "located at {0}.").format(locale_file_path)) diff --git a/tionix_vdi_client/tests/functional/func_test_vdi_client_server_connection.py b/tionix_vdi_client/tests/functional/func_test_vdi_client_server_connection.py index 4d69e10..9aac4c2 100644 --- a/tionix_vdi_client/tests/functional/func_test_vdi_client_server_connection.py +++ b/tionix_vdi_client/tests/functional/func_test_vdi_client_server_connection.py @@ -36,14 +36,14 @@ class VDIConnectionTest(unittest.TestCase): try: vdi_client = client.VdiClient(**self.conn_params) except Exception as err: - self.fail(unicode(err)) + self.fail(str(err)) try: # noinspection PyUnresolvedReferences with patch_dict(CONF, timeout=1, retries=1): vdi_client.request_vm() except exceptions.VDIServerResponseError as err: if err.code != exceptions.ErrorCodes.AUTH_ERROR: - self.fail(unicode(err)) + self.fail(str(err)) except Exception as err: - self.fail(_(u"Unexpected server response. " - u"{0}").format(unicode(err))) + self.fail(_("Unexpected server response. " + "{0}").format(str(err))) diff --git a/tionix_vdi_client/tests/functional/runner.py b/tionix_vdi_client/tests/functional/runner.py index 8866068..0440ee0 100644 --- a/tionix_vdi_client/tests/functional/runner.py +++ b/tionix_vdi_client/tests/functional/runner.py @@ -1,6 +1,6 @@ # coding=utf-8 -from StringIO import StringIO +from io import StringIO import traceback import unittest @@ -11,10 +11,10 @@ from tionix_vdi_client.i18n import ugettext as _ class TestResultStatus(object): - OK = _(u'OK') - ERROR = _(u'ERROR') - FAIL = _(u'FAIL') - SKIPPED = _(u'SKIPPED') + OK = _('OK') + ERROR = _('ERROR') + FAIL = _('FAIL') + SKIPPED = _('SKIPPED') # noinspection PyUnresolvedReferences @@ -22,7 +22,7 @@ class SkipTest(unittest.case.SkipTest): def __init__(self, reason=None): # convert unicode string to bytestring to prevent # UnicodeEncodeError in unittest.TestCase - if isinstance(reason, unicode): + if isinstance(reason, str): reason = reason.encode('utf-8') super(SkipTest, self).__init__(reason) @@ -44,11 +44,11 @@ class ExtendedTextTestResult(unittest.TestResult): if exctype is AssertionError: message = value.message if isinstance(message, str): - message = message.decode('utf8') + message = message.encode().decode('utf8') else: if not self.debug: tb = None - message = u''.join( + message = ''.join( traceback.format_exception(exctype, value, tb)) return message @@ -88,11 +88,11 @@ class ExtendedTextTestResult(unittest.TestResult): self.process_test( test, status=TestResultStatus.SKIPPED, details=reason) - def process_test(self, test, status, details=u''): + def process_test(self, test, status, details=''): """Process test and update results dict""" - test_name = unicode(test).split()[0].replace('_', ' ') + test_name = str(test).split()[0].replace('_', ' ') if isinstance(details, str): - details = details.decode('utf-8') + details = details.encode().decode('utf-8') self.tests_results.append((test_name, status, details)) diff --git a/tionix_vdi_client/tests/test_diagnostic/test_vdi_client_conf_files.py b/tionix_vdi_client/tests/test_diagnostic/test_vdi_client_conf_files.py index 861c801..1c131ca 100644 --- a/tionix_vdi_client/tests/test_diagnostic/test_vdi_client_conf_files.py +++ b/tionix_vdi_client/tests/test_diagnostic/test_vdi_client_conf_files.py @@ -1,7 +1,7 @@ # coding: utf-8 # noinspection PyPep8Naming -import ConfigParser as configparser +import configparser as configparser from mock import Mock, patch diff --git a/tionix_vdi_client/tests/unit/test_vdi_connect.py b/tionix_vdi_client/tests/unit/test_vdi_connect.py index aea7eb5..8062c74 100644 --- a/tionix_vdi_client/tests/unit/test_vdi_connect.py +++ b/tionix_vdi_client/tests/unit/test_vdi_connect.py @@ -39,7 +39,7 @@ class TestVdiClientAPIMethods(unittest.TestCase): @mock.patch('tionix_vdi_client.client.requests.post') def test_change_password_success(self, mocked_post): """Change password request sent and updated in client.""" - new_password = u'МойНовыйПар0ль' + new_password = 'МойНовыйПар0ль' response = stubs.Response(json={'msg': 'Password changed.'}) mocked_post.return_value = response @@ -84,7 +84,7 @@ class TestVdiClientAPIMethods(unittest.TestCase): @mock.patch('tionix_vdi_client.client.requests.post') def test_action_handle_server_errors(self, mocked_post): """Server-side errors are handled properly.""" - resp_codes_server_error = [400] + range(402, 431) + range(500, 512) + resp_codes_server_error = [400] + list(range(402, 431)) + list(range(500, 512)) for err_code in resp_codes_server_error: resp = stubs.Response(json={'message': 'Unknown test err.'}, @@ -252,10 +252,10 @@ class TestVDIConnect(unittest.TestCase): expected_call_params = self.default_auth_params.copy() expected_call_params.update({'vm_ip': '127.0.0.1', 'domain_name': 'default', - 'rdp_token': u'token'}) + 'rdp_token': 'token'}) self.vdi_client_mock.get_vm.return_value = VM( - vm, RequestStatus.COMPLETED, u'token') + vm, RequestStatus.COMPLETED, 'token') vd_client_mock.return_value = self.vdi_client_mock tionix_vdi_client.app_cli.main() diff --git a/tionix_vdi_client/tests/unit/test_writers.py b/tionix_vdi_client/tests/unit/test_writers.py index a2ac6e0..e369634 100644 --- a/tionix_vdi_client/tests/unit/test_writers.py +++ b/tionix_vdi_client/tests/unit/test_writers.py @@ -16,7 +16,7 @@ class TestWriters(unittest.TestCase): super(TestWriters, self).setUp() self.test_version = 'test_version' self.file_with_version = os.path.join( - tempfile.gettempdir(), u'vdi_client_version_file.txt') + tempfile.gettempdir(), 'vdi_client_version_file.txt') self.ORIGIN_FILE_WITH_VERSION = version_writter.FILE_WITH_VERSION version_writter.FILE_WITH_VERSION = self.file_with_version diff --git a/tionix_vdi_client/version_writter.py b/tionix_vdi_client/version_writter.py index 0878789..ad21af1 100644 --- a/tionix_vdi_client/version_writter.py +++ b/tionix_vdi_client/version_writter.py @@ -10,7 +10,7 @@ __version__ = u"{version}" __os_bitness__ = u"{bitness}" """ -FILE_WITH_VERSION = u'tionix_vdi_client/version.py' +FILE_WITH_VERSION = 'tionix_vdi_client/version.py' def get_win_os_bitness(): @@ -44,7 +44,7 @@ def write_version_to_file(): from pbr import packaging dist.Distribution = before_patch_class - version = packaging.get_version(u'tionix_vdi_client') + version = packaging.get_version('tionix_vdi_client') os_bitness = get_win_os_bitness() with open(FILE_WITH_VERSION, "w") as opened_file: opened_file.write(VERSION_FILE_CONTENT.format( diff --git a/tionix_vdi_client/win/devices.py b/tionix_vdi_client/win/devices.py index 148e761..95f86a0 100644 --- a/tionix_vdi_client/win/devices.py +++ b/tionix_vdi_client/win/devices.py @@ -5,7 +5,7 @@ import itertools # noinspection PyUnresolvedReferences def supported_device_classes(): - import _winreg as winreg + import winreg as winreg ids = [] try: reg_key = winreg.OpenKey( diff --git a/tionix_vdi_client/win/winapi.py b/tionix_vdi_client/win/winapi.py index 10cea4e..c52679e 100644 --- a/tionix_vdi_client/win/winapi.py +++ b/tionix_vdi_client/win/winapi.py @@ -42,7 +42,7 @@ def crypt_protect_data(plain_text): buffer_in = create_string_buffer(plain_text, len(plain_text)) blob_in = DataBlob(len(plain_text), buffer_in) blob_out = DataBlob() - desc = u'psw' + desc = 'psw' flags = CRYPTPROTECT_UI_FORBIDDEN | CRYPTPROTECT_LOCAL_MACHINE # The CryptProtectData function performs encryption on the data # in a DATA_BLOB structure.