Skip to content

Fixed many python compatiblity issues #900

New issue

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

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

Already on GitHub? Sign in to your account

Open
wants to merge 30 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
0f27b29
enhanced relative import "from ..foo.bar import a" + support of type …
kochelmonster Dec 6, 2021
bd45af3
implemented __init__ call of meta class.
kochelmonster Dec 6, 2021
6a08c92
extended core with id, frozenset and getattr with default argument,
kochelmonster Dec 6, 2021
b13751d
enhanced inspect.js with getmro.
kochelmonster Dec 6, 2021
207d63d
support of descriptor protocol.
kochelmonster Dec 7, 2021
b1b5a7b
new weakref module.
kochelmonster Dec 7, 2021
2ebe55a
new operator module.
kochelmonster Dec 7, 2021
d0ef2c9
new functools module.
kochelmonster Dec 7, 2021
4b4ca3e
new collections module.
kochelmonster Dec 7, 2021
928c0c0
new bisect module.
kochelmonster Dec 7, 2021
3029a0b
type(function) returns a function type.
kochelmonster Dec 21, 2021
3addc2e
added RuntimeError.
kochelmonster Dec 21, 2021
61dbc7b
fixed bug in __getattr__
kochelmonster Dec 21, 2021
bd7b980
fixed relative import of modules "from . import module" + tests for r…
kochelmonster Dec 23, 2021
4409aaa
minor fixes.
kochelmonster Dec 27, 2021
d121e1d
"$" joins for command line arguments do not work in linux -> allow mu…
kochelmonster Dec 29, 2021
653610c
fixes for strict mode.
kochelmonster Jan 1, 2022
21efd0f
functools update_wrapper
kochelmonster Jan 1, 2022
0894db4
fixed operator.itemgetter
kochelmonster Jan 3, 2022
e59fd63
__super__ now calls the correct mro.
kochelmonster Jan 7, 2022
bc1f3b4
enhanced tests for new __super__
kochelmonster Jan 7, 2022
f5000fd
Activated the method memoizing + keep attrbutes methods.
kochelmonster Jan 7, 2022
c4a751d
Activate method memoizing + keep attributes of method.
kochelmonster Jan 7, 2022
2dfc9ba
Merge branch 'master' of https://github.com/kochelmonster/Transcrypt
kochelmonster Jan 7, 2022
5c0c065
super handling: correct initialization of classes if __init__ is miss…
kochelmonster Jan 8, 2022
0f3eb6b
Another fix for super
kochelmonster Jan 9, 2022
72aee66
Improved metaclass handling.
kochelmonster Jan 9, 2022
ab150cd
Another fix for super.
kochelmonster Jan 18, 2022
7e77d81
Merge remote-tracking branch 'upstream/master'
kochelmonster Feb 19, 2025
2011f14
Merge remote-tracking branch 'upstream/master'
kochelmonster Jun 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 38 additions & 36 deletions transcrypt/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
sys.path = [item.replace ('\\', '/') for item in sys.path]

# Unload org from a packages dir, if it happens to be there in the CPython installation
sys.modules.pop ('org', None)
sys.modules.pop ('org', None)

# Transcrypt needs to find modulesDir before CPython modules, so it will favor Transcrypt modules
candidateTranspilationDirs = [modulesDir] + sys.path
Expand All @@ -49,7 +49,7 @@
# The following imports are needed by Transcrypt itself, not by transpiled or executed user code
# They will either reload the previously unloaded org or load org from different location
# CPython needs to find modulesDir after CPython modules, so it will favor CPython modules
sys.path.append (modulesDir)
sys.path.append (modulesDir)
from org.transcrypt import utils
from org.transcrypt import compiler
exitCodeNames = ('exitSuccess', 'exitCommandArgsError', 'exitNoLicense', 'exitSourceNotGiven', 'exitCannotRunSource', 'exitSpecificCompileError', 'exitGeneralCompileError')
Expand All @@ -62,17 +62,17 @@ def main ():

def exitHandler ():
if exitCode == exitSuccess:
utils.log (True, '\nReady\n\n')
utils.log (True, '\nReady\n\n')
else:
utils.log (True, '\nAborted\n\n')

atexit.register (exitHandler)

def setExitCode (anExitCode):
nonlocal exitCode
exitCode = anExitCode
return exitCode

try:
__envir__ = utils.Any ()
with tokenize.open (f'{modulesDir}/org/transcrypt/__envir__.js') as envirFile:
Expand All @@ -81,73 +81,75 @@ def setExitCode (anExitCode):

utils.log (True, '\n{} (TM) Python to JavaScript Small Sane Subset Transpiler Version {}\n', __envir__.transpiler_name.capitalize (), __envir__.transpiler_version)
utils.log (True, 'Copyright (C) Geatec Engineering. License: Apache 2.0\n\n')

utils.log (True, '\n')
licensePath = '{}/{}'.format (transpilerDir, 'license_reference.txt')
licensePath = '{}/{}'.format (transpilerDir, 'license_reference.txt')
if not os.path.isfile (licensePath):
utils.log (True, 'Error: missing license reference file\n')
return setExitCode (exitNoLicense)

utils.commandArgs.parse ()

if utils.commandArgs.license:
with open (licensePath) as licenseFile:
bar = 80 * '*'
utils.log (True, '\n{}\n\n', bar)
utils.log (True, '{}\n', licenseFile.read ())
utils.log (True, '{}\n\n', bar)

if utils.commandArgs.star:
webbrowser.open ('https://github.com/TranscryptOrg/Transcrypt')

if not utils.commandArgs.source:
return setExitCode (exitSourceNotGiven) # Should never be here, dealth with by command arg checks already

if utils.commandArgs.source.endswith ('.py') or utils.commandArgs.source.endswith ('.js'):
utils.commandArgs.source = utils.commandArgs.source [:-3] # Ignore extension
# This may be done in a more concise way, but it runs deeper than it may seem, so test any changes extensively

# Prepend paths that are needed by transpiled or executed user code, since they have to be searched first
# So user code favors Transcrypt modules over CPython modules
extraDirs = utils.commandArgs.xpath.replace ('#', ' ') .split ('$') if utils.commandArgs.xpath else []

extraDirs = sum((p.replace('#', ' ').split("$")
for p in (utils.commandArgs.xpath or [])), start=[])

sourcePath = utils.commandArgs.source.replace ('\\', '/') # May be absolute or relative, in the latter case it may or may not specify a directory
if '/' in sourcePath: # If directory specified
sourceDir = sourcePath.rsplit ('/', 1)[0] # Use it as source directory
else: # Else
sourceDir = os.getcwd () .replace ('\\', '/') # Use current working directory as source directory

projectDirs = [sourceDir] + extraDirs

sys.path [0 : 0] = projectDirs

global transpilationDirs
transpilationDirs [0 : 0] = projectDirs

__symbols__ = utils.commandArgs.symbols.split ('$') if utils.commandArgs.symbols else []

transpilationDirs [0 : 0] = projectDirs


__symbols__ = sum((s.split("$") for s in (utils.commandArgs.symbols or [])), start=[])

if utils.commandArgs.complex:
__symbols__.append ('__complex__')

if utils.commandArgs.sform:
__symbols__.append ('__sform__')

if utils.commandArgs.xtiny:
__symbols__.append ('__xtiny__')

__symbols__.append ('__py{}.{}__'.format (* sys.version_info [:2]))

if utils.commandArgs.esv:
__symbols__.append ('__esv{}__'.format (utils.commandArgs.esv))
else:
__symbols__.append ('__esv{}__'.format (utils.defaultJavaScriptVersion))

# Import (ignored when transpiling) late, since commandArgs must be set already
from org.transcrypt.stubs.browser import __set_stubsymbols__

# Make symbols available to CPython, seems that exec can't do that directly
__set_stubsymbols__ (__symbols__)

if utils.commandArgs.run:
try:
with open (utils.commandArgs.source + '.py') as sourceFile:
Expand All @@ -163,25 +165,25 @@ def setExitCode (anExitCode):
return setExitCode (exitSuccess)
except utils.Error as error:
utils.log (True, '\n{}\n', error)

# Don't log anything else, even in verbose mode, since this would only be confusing
if utils.commandArgs.dextex:
utils.log (True, '{}\n', traceback.format_exc ())

return setExitCode (exitSpecificCompileError)
except Exception as exception:
utils.log (True, '\n{}', exception)

# Have to log something else, because a general exception isn't informative enough
utils.log (True, '{}\n', traceback.format_exc ())

return setExitCode (exitGeneralCompileError)

except utils.CommandArgsError:
return setExitCode (exitCommandArgsError)

except utils.CommandArgsExit:
return setExitCode (exitSuccess)

if __name__ == '__main__':
sys.exit (main ())
7 changes: 7 additions & 0 deletions transcrypt/development/automated_tests/relimport/autotest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import org.transcrypt.autotester
import rimport

autoTester = org.transcrypt.autotester.AutoTester ()

autoTester.run(rimport, 'relative import')
autoTester.done()
6 changes: 6 additions & 0 deletions transcrypt/development/automated_tests/relimport/rimport.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import tpackage


def run(test):
test.check(type(tpackage.peer2.func).__name__)
test.check(type(tpackage.func1).__name__)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from . import peer as peer1
from .peer import func


peer2 = peer1
func1 = func
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def func():
pass
Original file line number Diff line number Diff line change
Expand Up @@ -7,48 +7,48 @@ class R:
def __init__ (self, a, b):
self.a = a
self.b = b

class A (R):
def __init__ (self, a, b, c):
super () .__init__ (a, b)
self.c = c

def f (self, x, y):
show ('A.f:', x, y, self.a, self.b, self.c)

def g (self, x, y):
show ('A.g:', x, y)

class B (R):
def __init__ (self, a, b, d):
super () .__init__ (a, b)
self.d = d

def f (self, x, y):
show ('B.f:', x, y, self.a, self.b, self.d)

def h (self, x, y):
show ('A.h:', x, y, self.a, self.b, self.d)

class C (A):
def __init__ (self, a, b, c):
super () .__init__ (a, b, c)

def f (self, x, y):
super () .f (x, y)
show ('C.f:', x, y, self.a, self.b, self.c)

class D (B):
def __init__ (self, a, b, d):
super () .__init__ (a, b, d)

def f (self, x, y):
super () .f (x, y)
show ('D.f:', x, y, self.a, self.b, self.d)

# Diamond inheritance, use super () only to call exactly one target method via unique path.
# In case of multiple target methods or multiple paths, don't use super (), but refer to ancestor classes explicitly instead
class E (C, D):
class E (C, D):
def __init__ (self, a, b, c, d):
R.__init__ (self, a, b) # Inherited via multiple legs of a diamond, so call explicitly
self.c = c # Could also have used C.__init__, but symmetry preferred
Expand All @@ -59,15 +59,15 @@ def f (self, x, y):
C.f (self, x, y) # Ambiguous part of diamond, don't use super ()
D.f (self, x, y) # Ambiguous part of diamond, don't use super ()
show ('E.f:', x, y, self.a, self.b, self.c, self.d)

def g (self, x, y):
super () .g (x, y) # Unique, using super () is OK
show ('E.g:', x, y, self.a, self.b, self.c, self.d)

def h (self, x, y):
super () .h (x, y) # Unique, using super () is OK
show ('E.h:', x, y, self.a, self.b, self.c, self.d)

rr = R (100, 200)

show ('--1--')
Expand Down Expand Up @@ -100,3 +100,70 @@ def h (self, x, y):
e.f (715, 815)
e.g (725, 825)
e.h (735, 835)

run_chain_test(autoTester)


def run_chain_test(autoTester):
class Z:
def __init__(self, call_order):
call_order.append("Z")
super().__init__(call_order)

class X(Z):
def __init__(self, call_order):
call_order.append("X")
super().__init__(call_order)

class Y:
def __init__(self, call_order):
call_order.append("Y")
super().__init__()

class W(X, Y):
def __init__(self, call_order):
call_order.append("W")
super().__init__(call_order)

class U(W):
def __init__(self, call_order):
call_order.append("U")
super().__init__(call_order)

call_order = []
U(call_order)
autoTester.check(call_order)

class Mixin:
def plugin(self, call_order):
call_order.append("Mixin")
super().plugin(call_order)

class Base:
def __init__(self, call_order):
call_order.append("Base")

def plugin(self, call_order):
call_order.append("Base")

class Child(Mixin, Base):
pass

class GrandChild(Child):
pass

call_order = []
mixed = Child(call_order)
autoTester.check (call_order)

call_order = []
mixed.plugin(call_order)
autoTester.check (call_order)

call_order = []
mixed = GrandChild(call_order)
autoTester.check (call_order)

call_order = []
mixed.plugin(call_order)
autoTester.check (call_order)
Loading