Skip to content

Commit

Permalink
Support importing string literal mod names
Browse files Browse the repository at this point in the history
trotterdylan committed Aug 22, 2017
1 parent 6bdfc4b commit 9d80504
Showing 25 changed files with 91 additions and 102 deletions.
10 changes: 4 additions & 6 deletions compiler/imputil.py
Original file line number Diff line number Diff line change
@@ -30,7 +30,7 @@
from grumpy.pythonparser import ast


_NATIVE_MODULE_PREFIX = '__go__.'
_NATIVE_MODULE_PREFIX = '__go__/'


class Import(object):
@@ -86,8 +86,8 @@ def visit_Import(self, node):
imports = []
for alias in node.names:
if alias.name.startswith(_NATIVE_MODULE_PREFIX):
raise util.ImportError(
node, 'for native imports use "from __go__.xyz import ..." syntax')
msg = 'for native imports use "from \'__go__/xyz\' import ..." syntax'
raise util.ImportError(node, msg)
imp = self._resolve_import(node, alias.name)
if alias.asname:
imp.add_binding(Import.MODULE, alias.asname, imp.name.count('.'))
@@ -100,9 +100,7 @@ def visit_Import(self, node):

def visit_ImportFrom(self, node):
if any(a.name == '*' for a in node.names):
msg = 'wildcard member import is not implemented: from %s import *' % (
node.module)
raise util.ImportError(node, msg)
raise util.ImportError(node, 'wildcard member import is not implemented')

if not node.level and node.module == '__future__':
return []
6 changes: 3 additions & 3 deletions compiler/imputil_test.py
Original file line number Diff line number Diff line change
@@ -188,18 +188,18 @@ def testImportFromFuture(self):
def testImportFromNative(self):
imp = imputil.Import('fmt', is_native=True)
imp.add_binding(imputil.Import.MEMBER, 'Printf', 'Printf')
self._check_imports('from __go__.fmt import Printf', [imp])
self._check_imports('from "__go__/fmt" import Printf', [imp])

def testImportFromNativeMultiple(self):
imp = imputil.Import('fmt', is_native=True)
imp.add_binding(imputil.Import.MEMBER, 'Printf', 'Printf')
imp.add_binding(imputil.Import.MEMBER, 'Println', 'Println')
self._check_imports('from __go__.fmt import Printf, Println', [imp])
self._check_imports('from "__go__/fmt" import Printf, Println', [imp])

def testImportFromNativeAs(self):
imp = imputil.Import('fmt', is_native=True)
imp.add_binding(imputil.Import.MEMBER, 'foo', 'Printf')
self._check_imports('from __go__.fmt import Printf as foo', [imp])
self._check_imports('from "__go__/fmt" import Printf as foo', [imp])

def testRelativeImportNonPackage(self):
self.assertRaises(util.ImportError, self.importer.visit,
14 changes: 2 additions & 12 deletions compiler/stmt.py
Original file line number Diff line number Diff line change
@@ -289,7 +289,7 @@ def visit_ImportFrom(self, node):
with self._import_native(imp.name, values) as mod:
for binding in imp.bindings:
# Strip the 'type_' prefix when populating the module. This means
# that, e.g. 'from __go__.foo import type_Bar' will populate foo
# that, e.g. 'from "__go__/foo" import type_Bar' will populate foo
# with a member called Bar, not type_Bar (although the symbol in
# the importing module will still be type_Bar unless aliased). This
# bends the semantics of import but makes native module contents
@@ -661,17 +661,7 @@ def _import_and_bind(self, imp):

def _import_native(self, name, values):
reflect_package = self.block.root.add_native_import('reflect')
# Work-around for importing go module from VCS
# TODO: support bzr|git|hg|svn from any server
package_name = None
for x in _KNOWN_VCS:
if name.startswith(x):
package_name = x + name[len(x):].replace('.', '/')
break
if not package_name:
package_name = name.replace('.', '/')

package = self.block.root.add_native_import(package_name)
package = self.block.root.add_native_import(name)
mod = self.block.alloc_temp()
with self.block.alloc_temp('map[string]*πg.Object') as members:
self.writer.write_tmpl('$members = map[string]*πg.Object{}',
19 changes: 9 additions & 10 deletions compiler/stmt_test.py
Original file line number Diff line number Diff line change
@@ -316,36 +316,35 @@ def testImportMember(self):
def testImportConflictingPackage(self):
self.assertEqual((0, ''), _GrumpRun(textwrap.dedent("""\
import time
from __go__.time import Now""")))
from "__go__/time" import Now""")))

def testImportNative(self):
self.assertEqual((0, '1 1000000000\n'), _GrumpRun(textwrap.dedent("""\
from __go__.time import Nanosecond, Second
from "__go__/time" import Nanosecond, Second
print Nanosecond, Second""")))

def testImportGrumpy(self):
self.assertEqual((0, ''), _GrumpRun(textwrap.dedent("""\
from __go__.grumpy import Assert
from "__go__/grumpy" import Assert
Assert(__frame__(), True, 'bad')""")))

def testImportNativeModuleRaises(self):
regexp = r'for native imports use "from __go__\.xyz import \.\.\." syntax'
regexp = (r'for native imports use '
r'"from \'__go__/xyz\' import \.\.\." syntax')
self.assertRaisesRegexp(util.ImportError, regexp, _ParseAndVisit,
'import __go__.foo')
'import "__go__/foo"')

def testImportNativeType(self):
self.assertEqual((0, "<type 'Duration'>\n"), _GrumpRun(textwrap.dedent("""\
from __go__.time import type_Duration as Duration
from "__go__/time" import type_Duration as Duration
print Duration""")))

def testImportWildcardMemberRaises(self):
regexp = r'wildcard member import is not implemented: from foo import *'
regexp = 'wildcard member import is not implemented'
self.assertRaisesRegexp(util.ImportError, regexp, _ParseAndVisit,
'from foo import *')
regexp = (r'wildcard member import is not '
r'implemented: from __go__.foo import *')
self.assertRaisesRegexp(util.ImportError, regexp, _ParseAndVisit,
'from __go__.foo import *')
'from "__go__/foo" import *')

def testPrintStatement(self):
self.assertEqual((0, 'abc 123\nfoo bar\n'), _GrumpRun(textwrap.dedent("""\
2 changes: 1 addition & 1 deletion lib/__builtin__.py
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@

# pylint: disable=invalid-name

from __go__.grumpy import Builtins
from '__go__/grumpy' import Builtins


for k, v in Builtins.iteritems():
6 changes: 3 additions & 3 deletions lib/_random.py
Original file line number Diff line number Diff line change
@@ -14,9 +14,9 @@

"""Generate pseudo random numbers. Should not be used for security purposes."""

from __go__.math.rand import Uint32, Seed
from __go__.math import Pow
from __go__.time import Now
from '__go__/math/rand' import Uint32, Seed
from '__go__/math' import Pow
from '__go__/time' import Now


BPF = 53 # Number of bits in a float
2 changes: 1 addition & 1 deletion lib/_syscall.py
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from __go__.syscall import EINTR
from '__go__/syscall' import EINTR


def invoke(func, *args):
2 changes: 1 addition & 1 deletion lib/exceptions.py
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@

"""Built-in exception classes."""

from __go__.grumpy import ExceptionTypes
from '__go__/grumpy' import ExceptionTypes


g = globals()
2 changes: 1 addition & 1 deletion lib/math.py
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from __go__.math import (Pi, E, Ceil, Copysign, Abs, Floor, Mod, Frexp, IsInf,
from '__go__/math' import (Pi, E, Ceil, Copysign, Abs, Floor, Mod, Frexp, IsInf,
IsNaN, Exp2, Modf, Trunc, Exp, Expm1, Log, Log1p, Log10, Pow, Sqrt, Acos,
Asin, Atan, Atan2, Hypot, Sin, Cos, Tan, Acosh, Asinh, Atanh, Sinh, Cosh,
Tanh, Erf, Erfc, Gamma, Lgamma) # pylint: disable=g-multiple-import
18 changes: 9 additions & 9 deletions lib/os/__init__.py
Original file line number Diff line number Diff line change
@@ -15,18 +15,18 @@
"""Miscellaneous operating system interfaces."""

# pylint: disable=g-multiple-import
from __go__.io.ioutil import ReadDir
from __go__.os import (Chdir, Chmod, Environ, Getpid as getpid, Getwd, Pipe,
from '__go__/io/ioutil' import ReadDir
from '__go__/os' import (Chdir, Chmod, Environ, Getpid as getpid, Getwd, Pipe,
type_ProcAttr as ProcAttr, Remove, StartProcess, Stat, Stdout, Stdin,
Stderr, Mkdir)
from __go__.path.filepath import Separator
from __go__.grumpy import (NewFileFromFD, StartThread, ToNative)
from __go__.reflect import MakeSlice
from __go__.runtime import GOOS
from __go__.syscall import (Close, SYS_FCNTL, Syscall, F_GETFD, Wait4,
from '__go__/path/filepath' import Separator
from '__go__/grumpy' import (NewFileFromFD, StartThread, ToNative)
from '__go__/reflect' import MakeSlice
from '__go__/runtime' import GOOS
from '__go__/syscall' import (Close, SYS_FCNTL, Syscall, F_GETFD, Wait4,
type_WaitStatus as WaitStatus, WNOHANG)
from __go__.sync import type_WaitGroup as WaitGroup
from __go__.time import Second
from '__go__/sync' import type_WaitGroup as WaitGroup
from '__go__/time' import Second
import _syscall
from os import path
import stat as stat_module
4 changes: 2 additions & 2 deletions lib/os/path.py
Original file line number Diff line number Diff line change
@@ -14,8 +14,8 @@

""""Utilities for manipulating and inspecting OS paths."""

from __go__.os import Stat
from __go__.path.filepath import Abs, Base, Clean, Dir as dirname, IsAbs as isabs, Join, Split # pylint: disable=g-multiple-import,unused-import
from '__go__/os' import Stat
from '__go__/path/filepath' import Abs, Base, Clean, Dir as dirname, IsAbs as isabs, Join, Split # pylint: disable=g-multiple-import,unused-import


def abspath(path):
2 changes: 1 addition & 1 deletion lib/select_.py
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from __go__.syscall import (
from '__go__/syscall' import (
FD_SETSIZE as _FD_SETSIZE,
Select as _Select,
type_FdSet as _FdSet,
2 changes: 1 addition & 1 deletion lib/stat.py
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@
"""Interpreting stat() results."""

# pylint: disable=g-multiple-import
from __go__.os import ModeDir, ModePerm
from '__go__/os' import ModeDir, ModePerm


def S_ISDIR(mode): # pylint: disable=invalid-name
8 changes: 4 additions & 4 deletions lib/sys.py
Original file line number Diff line number Diff line change
@@ -14,10 +14,10 @@

"""System-specific parameters and functions."""

from __go__.os import Args
from __go__.grumpy import SysModules, MaxInt, Stdin as stdin, Stdout as stdout, Stderr as stderr # pylint: disable=g-multiple-import
from __go__.runtime import (GOOS as platform, Version)
from __go__.unicode import MaxRune
from '__go__/os' import Args
from '__go__/grumpy' import SysModules, MaxInt, Stdin as stdin, Stdout as stdout, Stderr as stderr # pylint: disable=g-multiple-import
from '__go__/runtime' import (GOOS as platform, Version)
from '__go__/unicode' import MaxRune

argv = []
for arg in Args:
4 changes: 2 additions & 2 deletions lib/tempfile.py
Original file line number Diff line number Diff line change
@@ -15,8 +15,8 @@
"""Generate temporary files and directories."""

# pylint: disable=g-multiple-import
from __go__.io.ioutil import TempDir, TempFile
from __go__.syscall import Dup
from '__go__/io/ioutil' import TempDir, TempFile
from '__go__/syscall' import Dup


# pylint: disable=redefined-builtin
2 changes: 1 addition & 1 deletion lib/thread.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from __go__.grumpy import NewTryableMutex, StartThread, ThreadCount
from '__go__/grumpy' import NewTryableMutex, StartThread, ThreadCount


class error(Exception):
2 changes: 1 addition & 1 deletion lib/time.py
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@

"""Time access and conversions."""

from __go__.time import Local, Now, Second, Sleep, Unix, Date, UTC # pylint: disable=g-multiple-import
from '__go__/time' import Local, Now, Second, Sleep, Unix, Date, UTC # pylint: disable=g-multiple-import


_strftime_directive_map = {
4 changes: 2 additions & 2 deletions lib/types_test.py
Original file line number Diff line number Diff line change
@@ -14,8 +14,8 @@

import types

from __go__.grumpy import (FunctionType, MethodType, ModuleType, StrType, # pylint: disable=g-multiple-import
TracebackType, TypeType)
from '__go__/grumpy' import (FunctionType, MethodType, ModuleType, StrType, # pylint: disable=g-multiple-import
TracebackType, TypeType)

# Verify a sample of all types as a sanity check.
assert types.FunctionType is FunctionType
46 changes: 16 additions & 30 deletions runtime/module.go
Original file line number Diff line number Diff line change
@@ -163,42 +163,28 @@ func ImportModule(f *Frame, name string) ([]*Object, *BaseException) {
return result, nil
}

// ImportNativeModule takes a fully qualified native module name (e.g.
// grumpy.native.fmt) and a mapping of module members that will be used to
// populate the module. The same logic is used as ImportModule for looking in
// sys.modules first. The last module created in this way is populated with the
// given members and returned.
// ImportNativeModule looks up or creates a native module corresponding to the
// named Go package (e.g. __go__/net/http). It populates the module using the
// map of members given.
func ImportNativeModule(f *Frame, name string, members map[string]*Object) (*Object, *BaseException) {
parts := strings.Split(name, ".")
numParts := len(parts)
var prev *Object
for i := 0; i < numParts; i++ {
name := strings.Join(parts[:i+1], ".")
importMutex.Lock()
o, raised := SysModules.GetItemString(f, name)
if raised == nil && o == nil {
m := newModule(name, "<native>")
m.state = moduleStateReady
o = m.ToObject()
raised = SysModules.SetItemString(f, name, o)
}
importMutex.Unlock()
if raised != nil {
return nil, raised
}
if prev != nil {
if raised := SetAttr(f, prev, NewStr(parts[i]), o); raised != nil {
return nil, raised
}
}
prev = o
importMutex.Lock()
o, raised := SysModules.GetItemString(f, name)
if raised == nil && o == nil {
m := newModule(name, "<native>")
m.state = moduleStateReady
o = m.ToObject()
raised = SysModules.SetItemString(f, name, o)
}
importMutex.Unlock()
if raised != nil {
return nil, raised
}
for k, v := range members {
if raised := SetAttr(f, prev, NewStr(k), v); raised != nil {
if raised := SetAttr(f, o, NewStr(k), v); raised != nil {
return nil, raised
}
}
return prev, nil
return o, nil
}

// newModule creates a new Module object with the given fully qualified name
10 changes: 5 additions & 5 deletions testing/native_test.py
Original file line number Diff line number Diff line change
@@ -14,11 +14,11 @@

# pylint: disable=g-multiple-import

from __go__.math import MaxInt32, Pow10, Signbit
from __go__.strings import Count, IndexAny, Repeat
from __go__.encoding.csv import NewReader as NewCSVReader
from __go__.image import Pt
from __go__.strings import NewReader as NewStringReader
from '__go__/math' import MaxInt32, Pow10, Signbit
from '__go__/strings' import Count, IndexAny, Repeat
from '__go__/encoding/csv' import NewReader as NewCSVReader
from '__go__/image' import Pt
from '__go__/strings' import NewReader as NewStringReader

assert Count('foo,bar,baz', ',') == 2
assert IndexAny('foobar', 'obr') == 1
2 changes: 1 addition & 1 deletion third_party/ouroboros/operator.py
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@
'neg', 'not_', 'or_', 'pos', 'pow', 'rshift', 'setitem', 'sub',
'truediv', 'truth', 'xor']

from __go__.math import Abs as _abs
from '__go__/math' import Abs as _abs


# Comparison Operations *******************************************************#
20 changes: 18 additions & 2 deletions third_party/pythonparser/parser.py
Original file line number Diff line number Diff line change
@@ -1035,11 +1035,15 @@ def import_from_5(self, star_loc):
name_loc=star_loc, as_loc=None, asname_loc=None, loc=star_loc)], \
None

@action(Rule("atom_5"))
def import_from_7(self, string):
return (None, 0), (string.loc, string.s)

@action(Rule("import_as_names"))
def import_from_6(self, names):
return (None, 0), names, None

@action(Seq(Loc("from"), Alt(import_from_3, import_from_4),
@action(Seq(Loc("from"), Alt(import_from_3, import_from_4, import_from_7),
Loc("import"), Alt(import_from_5,
Seq(Loc("("), Rule("import_as_names"), Loc(")")),
import_from_6)))
@@ -1097,10 +1101,22 @@ def dotted_as_name(self, dotted_name, as_name_opt):
return ast.alias(name=dotted_name_name, asname=asname_name,
loc=loc, name_loc=dotted_name_loc, as_loc=as_loc, asname_loc=asname_loc)

@action(Seq(Rule("atom_5"), Opt(Seq(Loc("as"), Tok("ident")))))
def str_as_name(self, string, as_name_opt):
asname_name = asname_loc = as_loc = None
loc = string.loc
if as_name_opt:
as_loc, asname = as_name_opt
asname_name = asname.value
asname_loc = asname.loc
loc = loc.join(asname.loc)
return ast.alias(name=string.s, asname=asname_name,
loc=loc, name_loc=string.loc, as_loc=as_loc, asname_loc=asname_loc)

import_as_names = List(Rule("import_as_name"), ",", trailing=True)
"""import_as_names: import_as_name (',' import_as_name)* [',']"""

dotted_as_names = List(Rule("dotted_as_name"), ",", trailing=False)
dotted_as_names = List(Alt(Rule("dotted_as_name"), Rule("str_as_name")), ",", trailing=False)
"""dotted_as_names: dotted_as_name (',' dotted_as_name)*"""

@action(List(Tok("ident"), ".", trailing=False))
2 changes: 1 addition & 1 deletion third_party/stdlib/_weakrefset.py
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@
# This code is separated-out because it is needed
# by abc.py to load everything else at startup.

from __go__.grumpy import WeakRefType as ref
from '__go__/grumpy' import WeakRefType as ref

__all__ = ['WeakSet']

2 changes: 1 addition & 1 deletion third_party/stdlib/copy.py
Original file line number Diff line number Diff line change
@@ -48,7 +48,7 @@ class instances).
"pickle" for information on these methods.
"""

from __go__.grumpy import WeakRefType
from '__go__/grumpy' import WeakRefType
import types
#from copy_reg import dispatch_table
import copy_reg
2 changes: 1 addition & 1 deletion third_party/stdlib/weakref.py
Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@
# ProxyType,
# ReferenceType)

from __go__.grumpy import WeakRefType as ReferenceType
from '__go__/grumpy' import WeakRefType as ReferenceType
ref = ReferenceType

import _weakrefset

0 comments on commit 9d80504

Please sign in to comment.