fix(python): retain facade types without phpy loaded (#128) --skip-tests
parent
c7cdc8b23e
commit
69e2a29ec2
8 changed files with 296 additions and 7 deletions
@ -0,0 +1,24 @@ |
||||
<?php |
||||
|
||||
class PyObject |
||||
{ |
||||
public function toValue(): int |
||||
{ |
||||
return 42; |
||||
} |
||||
} |
||||
|
||||
class PyList extends PyObject |
||||
{ |
||||
public function toArray(): array |
||||
{ |
||||
return [1, 2]; |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$value = new PyObject(); |
||||
$list = new PyList(); |
||||
var_dump($value->toValue(), $list->toArray()); |
||||
} |
||||
@ -0,0 +1,14 @@ |
||||
<?php |
||||
|
||||
function main(): void |
||||
{ |
||||
$list = python\list([1, 2, 3]); |
||||
$count = $list->count(); |
||||
$contains = $list->contains(2); |
||||
$slice = $list->slice(0, 2)->toArray(); |
||||
$set = python\set([1, 2]); |
||||
$member = $set->contains(1); |
||||
$value = python\int(42); |
||||
$value = $list; |
||||
var_dump($count, $contains, $slice, $member, $value->toArray()); |
||||
} |
||||
@ -0,0 +1,63 @@ |
||||
<?php |
||||
|
||||
namespace TypePhp\Tests\Python; |
||||
|
||||
use PHPUnit\Framework\TestCase; |
||||
|
||||
final class PythonWithoutExtensionTest extends TestCase |
||||
{ |
||||
public function testPythonCodeGenerationDoesNotRequirePhpy(): void |
||||
{ |
||||
// CI normally loads phpy. Start a clean compiler host to keep this contract covered. |
||||
$command = [PHP_BINARY, '-n']; |
||||
foreach (['ctype', 'mbstring', 'tokenizer'] as $extension) { |
||||
$path = ini_get('extension_dir') . DIRECTORY_SEPARATOR |
||||
. (PHP_OS_FAMILY === 'Windows' ? 'php_' : '') . $extension . '.' . PHP_SHLIB_SUFFIX; |
||||
if (is_file($path)) { |
||||
$command[] = '-d'; |
||||
$command[] = 'extension=' . $path; |
||||
} |
||||
} |
||||
$command[] = '-r'; |
||||
$command[] = <<<'PHP' |
||||
if (extension_loaded('phpy')) { |
||||
throw new RuntimeException('The compiler host must not load phpy'); |
||||
} |
||||
require 'phpunit/bootstrap.php'; |
||||
$result = []; |
||||
foreach (['operators', 'object-protocol', 'object-conversion-methods', 'facade-methods', 'facade-class-name-collision'] as $fixture) { |
||||
$translator = TypePhp\CompilerTest::create(TYPEPHP_ROOT_PATH); |
||||
$source = TYPEPHP_ROOT_PATH . '/phpunit/code/python/' . $fixture . '.php'; |
||||
$translator->addFiles([$source]); |
||||
$translator->prepareFile($source); |
||||
$result[$fixture] = file_get_contents($translator->convertFile($source)) |
||||
. file_get_contents($translator->genExtension()); |
||||
} |
||||
echo "\nTYPEPHP_RESULT:" . json_encode($result, JSON_THROW_ON_ERROR); |
||||
PHP; |
||||
$process = proc_open($command, [ |
||||
0 => ['pipe', 'r'], |
||||
1 => ['pipe', 'w'], |
||||
2 => ['pipe', 'w'], |
||||
], $pipes, TYPEPHP_ROOT_PATH); |
||||
self::assertIsResource($process); |
||||
fclose($pipes[0]); |
||||
$stdout = stream_get_contents($pipes[1]); |
||||
fclose($pipes[1]); |
||||
$stderr = stream_get_contents($pipes[2]); |
||||
fclose($pipes[2]); |
||||
self::assertSame(0, proc_close($process), $stdout . $stderr); |
||||
self::assertSame(1, preg_match('/\nTYPEPHP_RESULT:(.*)\z/s', $stdout, $matches), $stdout); |
||||
$generated = json_decode($matches[1], true, flags: JSON_THROW_ON_ERROR); |
||||
|
||||
self::assertStringNotContainsString('php::python::', $generated['facade-class-name-collision']); |
||||
self::assertStringContainsString('iadd', $generated['operators']); |
||||
self::assertStringContainsString('is_not', $generated['operators']); |
||||
self::assertStringContainsString('php::python::call(', $generated['object-protocol']); |
||||
self::assertStringContainsString('php::python::toArray(', $generated['object-conversion-methods']); |
||||
self::assertStringNotContainsString('php::toArray(', $generated['object-conversion-methods']); |
||||
self::assertStringContainsString('php::Var value;', $generated['object-conversion-methods']); |
||||
self::assertStringContainsString('php::python::toArray(', $generated['facade-methods']); |
||||
self::assertStringNotContainsString('php::python::callMember(', $generated['facade-methods']); |
||||
} |
||||
} |
||||
@ -0,0 +1,42 @@ |
||||
--TEST-- |
||||
Python facade methods retain their PHP return types and inherited behavior |
||||
--SKIPIF-- |
||||
<?php |
||||
if (!extension_loaded('phpy')) { |
||||
die('skip phpy extension is not loaded'); |
||||
} |
||||
?> |
||||
--FILE-- |
||||
<?php |
||||
|
||||
function main(): void |
||||
{ |
||||
$list = python\list([1, 2, 3]); |
||||
$count = $list->count(); |
||||
$contains = $list->contains(2); |
||||
$slice = $list->slice(0, 2)->toArray(); |
||||
$set = python\set([1, 2]); |
||||
$member = $set->contains(1); |
||||
$value = python\int(42); |
||||
$value = $list; |
||||
var_dump($count, $contains, $slice, $member, $value->toArray()); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
int(3) |
||||
bool(true) |
||||
array(2) { |
||||
[0]=> |
||||
int(1) |
||||
[1]=> |
||||
int(2) |
||||
} |
||||
bool(true) |
||||
array(3) { |
||||
[0]=> |
||||
int(1) |
||||
[1]=> |
||||
int(2) |
||||
[2]=> |
||||
int(3) |
||||
} |
||||
Loading…
Reference in new issue