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