Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
Prev Previous commit
Next Next commit
add a Variables method
  • Loading branch information
yagweb committed Apr 7, 2017
commit 904d9ed168a31c83438fdbf2f3838ef528bf71cb
31 changes: 31 additions & 0 deletions src/embed_tests/TestPyScope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,37 @@ public void TestScopeFunction()
}
}

/// <summary>
/// Create a class in the scope, the class can read variables in the scope.
/// Its methods can write the variables with the help of 'global' keyword.
/// </summary>
[Test]
public void TestScopeClass()
{
using (Py.GIL())
{
dynamic _ps = ps;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this dynamic scope necessary?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, I see below, you dynamically accessed the class

_ps.bb = 100;
ps.Exec(
"class class1():\n" +
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should read class Class1: to be idiomatic python. Also, is there any particular reason for not using multi-line strings?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revised.

" def __init__(self, value):\n" +
" self.value = value\n" +
" def call(self, arg):\n" +
" return self.value + bb + arg\n" + //use scope variables
" def update(self, arg):\n" +
" global bb\n" +
" bb = self.value + arg\n" //update scope variable
);
dynamic obj1 = _ps.class1(20);
var result = obj1.call(10).AsManagedObject(typeof(int));
Assert.AreEqual(130, result);

obj1.update(10);
result = ps.GetVariable<int>("bb");
Assert.AreEqual(30, result);
}
}

/// <summary>
/// Import a python module into the session.
/// Equivalent to the Python "import" statement.
Expand Down
69 changes: 49 additions & 20 deletions src/runtime/pyscope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ public PyScopeException(string message)
}
}

/// <summary>
/// Classes implement this interface must be used with GIL obtained.
/// </summary>
public interface IPyObject : IDisposable
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a very weird naming for what you are trying to mark here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace this interface with Attritue or just remove it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Attribute would make more sense, yes. Could allow us to test this at least at runtime.

{
}
Expand All @@ -38,6 +41,8 @@ internal PyScope(string name)
variables, "__builtins__",
Runtime.PyEval_GetBuiltins()
);
SetVariable("locals", (Func<PyDict>)Variables);
SetVariable("globals", (Func<PyDict>)Variables);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As said before, I'm strongly against this. I don't see any advantage in this over separating globals and locals as done in the Python API.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the responses below.

}

/// <summary>
Expand All @@ -47,6 +52,11 @@ internal PyScope(string name)

public event Action<PyScope> OnDispose;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the use-case for this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PyScopes are managed by Py class, when it was disposed, Py class can do some cleaning work through registering this event.

In the future, methods in Py class maybe put in an independent ScopeManager class, see the responses below.


public PyDict Variables()
{
return new PyDict(variables);
}

public PyScope CreateScope()
{
var scope = new PyScope(null);
Expand Down Expand Up @@ -276,17 +286,26 @@ private PyObject _GetVariable(string name)
public PyObject GetVariable(string name)
{
Check();
var variable = _GetVariable(name);
if (variable == null)
{
throw new PyScopeException(String.Format("'ScopeStorage' object has no attribute '{0}'", name));
}
if (variable.Handle == Runtime.PyNone)
using (var pyKey = new PyString(name))
{
variable.Dispose();
return null;
if (Runtime.PyMapping_HasKey(variables, pyKey.obj) != 0)
{
IntPtr op = Runtime.PyObject_GetItem(variables, pyKey.obj);
if (op == IntPtr.Zero)
{
throw new PythonException();
}
if (op == Runtime.PyNone)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaks a reference. Also, I'm not sure whether this is the right place for this conversion.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

{
return null;
}
return new PyObject(op);
}
else
{
throw new PyScopeException(String.Format("'ScopeStorage' object has no attribute '{0}'", name));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a weird error message. Also, why is this not implemented in terms of TryGetVariable?

Copy link
Contributor Author

@yagweb yagweb Apr 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be compatible with IronPython, see the responses below.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have no compatibility with IronPython here anyhow. ScopeStorage doesn't exist, this error is extremely confusing to me. And I still don't get why this does not just call TryGetVariable, raising an exception if the result is false.

}
}
return variable;
}

/// <summary>
Expand All @@ -299,19 +318,29 @@ public PyObject GetVariable(string name)
public bool TryGetVariable(string name, out PyObject value)
{
Check();
var variable = _GetVariable(name);
if (variable == null)
{
value = null;
return false;
}
if (variable.Handle == Runtime.PyNone)
using (var pyKey = new PyString(name))
{
value = null;
return true;
if (Runtime.PyMapping_HasKey(variables, pyKey.obj) != 0)
{
IntPtr op = Runtime.PyObject_GetItem(variables, pyKey.obj);
if (op == IntPtr.Zero)
{
throw new PythonException();
}
if (op == Runtime.PyNone)
{
value = null;
return true;
}
value = new PyObject(op);
return true;
}
else
{
value = null;
return false;
}
}
value = variable;
return true;
}

public T GetVariable<T>(string name)
Expand Down