Skip to content
This repository has been archived by the owner on Jul 16, 2024. It is now read-only.

Install pgvector on CI #225

Merged
merged 47 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
dafaf19
Install pgvector on CI
Nov 8, 2023
ffeec29
Build custom images for server
Nov 9, 2023
e87bf84
Clean code
Nov 9, 2023
7f36dd9
Fix dependencies
Nov 9, 2023
bc7a67e
Clean again
Nov 9, 2023
1333f38
Print debug message
Nov 9, 2023
51280b2
Fix dependencies again
Nov 9, 2023
8f935a5
Specify postgres user
Nov 9, 2023
44f92e0
Fix bracket
Nov 9, 2023
52e5f02
Fix permission issue
Nov 9, 2023
d32d229
Remove services
Nov 9, 2023
a548b1f
Fix errors in lint and doc
Nov 10, 2023
db1461f
Skip install for doc
Nov 10, 2023
ee155c1
Remove skip install for doc again
Nov 10, 2023
239491f
Add VERSION to MANIFEST.in
Nov 10, 2023
5d3f11e
Cusomize env with tox --override
Nov 10, 2023
bf0af2e
Fix tox errors
Nov 10, 2023
dcb9595
Fix variable errors
Nov 10, 2023
c6866b9
Fix error on docker env
Nov 10, 2023
1bd2eed
Fix tox variable issue again
Nov 10, 2023
d0c315c
Remove basepython
Nov 10, 2023
b1a5833
Remove all basepython
Nov 10, 2023
54ddd1b
Fix typo
Nov 10, 2023
f496178
Chnage back to 3.11
Nov 10, 2023
78eb7d3
Fix bug on get_type_hints()
Nov 10, 2023
a059f21
Add option to use dill
Nov 11, 2023
db28bb9
Use tox-docker to run server
Nov 12, 2023
5a9cfbf
Remove password
Nov 12, 2023
a56034c
Move healthcheck to dockerfile
Nov 12, 2023
3e744d0
Create Python venv
Nov 12, 2023
b1117a5
Install python3-venv
Nov 12, 2023
5b19116
Ignore test cases for pickler
Nov 12, 2023
51214ce
Fix missing newline
Nov 12, 2023
2d8fefb
Install dill when using pickler
Nov 13, 2023
06e876c
Fix lint error
Nov 13, 2023
2939c7b
Make dill test optional
Nov 13, 2023
7f8a0ab
Try to fix if in jobs
Nov 13, 2023
6ed522c
Test if
Nov 13, 2023
e992dc3
Retry if
Nov 13, 2023
893995d
Try fix if again
Nov 13, 2023
98566f5
Use format() in if
Nov 13, 2023
58a0cfe
Use single quote
Nov 13, 2023
1daa435
FIx format string
Nov 13, 2023
ecfde8a
Clean github actions config
Nov 13, 2023
c0b021e
Fix error in variables
Nov 13, 2023
32e7172
Fix wrong server config
Nov 13, 2023
226e623
Add doc
Nov 13, 2023
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
2 changes: 1 addition & 1 deletion .github/workflows/build_docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
- name: Build the docs
run: |
export TAG_REF=${{ github.ref }}
tox -e docs
tox -e doc
Copy link
Contributor

Choose a reason for hiding this comment

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

nice change

- name: Save ref
run: echo "${{ github.ref }}" >> build/doc/ref.txt

Expand Down
89 changes: 25 additions & 64 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Test Postgres
name: Test

on:
push:
Expand All @@ -8,81 +8,42 @@ on:
paths-ignore:
- "*.md"

env:
PGPASSWORD: postgres
TESTDB: greenplum_python_test
PGUSER: postgres

jobs:
build:
name: Test Python${{ matrix.python-version }}, PSQL${{matrix.postgres-version}}
name: python${{ matrix.python-version }}; ${{ matrix.server }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
fail-fast: true
matrix:
python-version: ["3.9", "3.10"]
postgres-version: ['12', '13']
python-version: ["3.9", "3.11"]
server: ["postgres12-python39", "postgres12-python311"]
include:
Copy link
Contributor

Choose a reason for hiding this comment

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

did you host the container in dockerhub?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, the image is built locally when running tox -e test-container.

Comment on lines +18 to +19
Copy link
Contributor

Choose a reason for hiding this comment

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

python3.9 and python3.11 pg-3.9 and pg-3.11 these are four different jobs am I right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, 4 jobs in total.

- tox_test: "test_py39"
python-version: "3.9"
- tox_test: "test_py310"
python-version: "3.10"
- dill_version: python3.9
postgres-version: '12'
- dill_version: python3.9
postgres-version: '13'

services:
postgres:
# we need postgres with plpython extension postgres base docker hub image did not have that
# I use this one
# image: yihong0618/postgres-plpython:${{ matrix.postgres-version }}
image: thorinschiffer/postgres-plpython:${{ matrix.postgres-version }}
env:
POSTGRES_USER: ${{ env.PGUSER }}
POSTGRES_PASSWORD: ${{ env.PGPASSWORD }}
POSTGRES_DB: ${{ env.TESTDB }}
ports:
- 5432:5432
options: --name postgres
- server: "postgres12-python39"
server-python-version: "3.9"
- server: "postgres12-python311"
server-python-version: "3.11"

steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
- name: Setup python on client
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
cache: pip
- name: Create plpython3u language and environment
run: |
psql postgres://${{ env.PGUSER }}:${{ env.PGPASSWORD }}@localhost/${{ env.TESTDB }} -c "CREATE LANGUAGE plpython3u;"
- name: Install Dependencies
run: |
pip install tox==3.25.0

- name: List all containers
run: docker ps -a

- name: Install Python in PostgreSQL server container
run: |
docker exec --user 0 postgres sh -c 'apt-get update && apt-get install -y python3-pip && mkdir -p $(su --login postgres --session-command "python3 -m site --user-site") && chown -R postgres /var/lib/postgresql'

- name: Run Tests
run: |
export POSTGRES_PASSWORD=${{ env.PGPASSWORD }}
export POSTGRES_USER=${{ env.PGUSER }}
export POSTGRES_DB=${{ env.TESTDB }}
tox -e ${{ matrix.tox_test }}

- name: Install Dependencies for greenplumpython dill
# plpytyhon for postgres install to pg services
- name: Run tests without pickler
run: |
pip install pip install --target=. dill
docker cp dill $(docker ps -q):/usr/lib/${{ matrix.dill_version }}/
rm -rf dill pip
- name: Run Tests with dill
python3 -m pip install tox~=4.11 tox-docker~=4.1 && \
tox \
--override=docker:server.dockerfile=server/${{ matrix.server }}.Dockerfile \
-e test-container \
-- \
--override-ini=server_use_pickler=false \
--ignore=tests/test_use_pickler.py \
- name: Run tests with pickler if python versions match
if: ${{ matrix.python-version == matrix.server-python-version }}
run: |
export POSTGRES_PASSWORD=${{ env.PGPASSWORD }}
export POSTGRES_USER=${{ env.PGUSER }}
export POSTGRES_DB=${{ env.TESTDB }}
tox -e ${{ matrix.tox_test }}
tox \
--override=docker:server.dockerfile=server/${{ matrix.server }}.Dockerfile \
-e test-container \
-- \
--override-ini=server_use_pickler=true \
10 changes: 0 additions & 10 deletions BUILD.md

This file was deleted.

3 changes: 1 addition & 2 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
include versioneer.py
include greenplumpython/_version.py
include greenplumpython/VERSION
12 changes: 6 additions & 6 deletions README.dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

### [tox](https://tox.wiki)

Install with `pip`:
We use tox as the task runner. Tox can be installed with

```
pip install tox
python3 -m pip install tox
```

Install with `brew` on macOS:
Expand Down Expand Up @@ -36,16 +36,16 @@ The tests will create connection to the Greenplum cluster. So the `PGPORT` needs
export PGPORT=6000
```

Test with the default python version:
Test with the default python version and a local database server:

```
tox -e test
```

Test with specified officially supported version:

To run tests against a database server in container:
```
tox -e test_py39
python3 -m pip install tox-docker
tox -e test-container
Copy link
Collaborator

Choose a reason for hiding this comment

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

Does this start a container automatically? It doesn't with my testing. How is this supposed to work? like: building image->creating container->running test?

Copy link
Contributor Author

@xuebinsu xuebinsu Nov 13, 2023

Choose a reason for hiding this comment

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

Yes, it will start the container automatically before running tests.

From the CI build, we should see the following lines when running tox -e test-container:

test-container: docker> build server/postgres12-python39.Dockerfile
test-container: docker> built: sha256:ae44ebcefd17
test-container: docker> run 'sha256:ae44ebcefd17' (from 'server')
test-container: docker> health check None (from 'server')

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The workflow is as follows:

  1. build image
  2. run container
  3. perform health check
  4. run tests
  5. stop and remove the container

Copy link
Collaborator

Choose a reason for hiding this comment

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

It fails on my machine:

GreenplumPython on  ci_pgvector [$!?] via 🐍 v3.11.5 (venv311)
❯ tox -e test-container
.pkg: _optional_hooks> python /usr/lib/python3.11/site-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
.pkg: get_requires_for_build_sdist> python /usr/lib/python3.11/site-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
.pkg: get_requires_for_build_wheel> python /usr/lib/python3.11/site-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
.pkg: prepare_metadata_for_build_wheel> python /usr/lib/python3.11/site-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
.pkg: build_sdist> python /usr/lib/python3.11/site-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
test-container: install_package> python -I -m pip install --force-reinstall --no-deps /home/cc/repo/GreenplumPython/.tox/.tmp/package/3/greenplum-python-1.0.1.tar.gz
test-container: commands[0]> pytest --exitfirst
============================================================= test session starts =============================================================
platform linux -- Python 3.11.5, pytest-7.0.1, pluggy-1.3.0
cachedir: .tox/test-container/.pytest_cache
rootdir: /home/cc/repo/GreenplumPython, configfile: tox.ini, testpaths: tests, greenplumpython
collected 241 items

tests/test_binaryExpr.py E

=================================================================== ERRORS ====================================================================
__________________________________________________ ERROR at setup of test_expr_bin_equal_int __________________________________________________

server_use_pickler = True

    @pytest.fixture(scope="session")
    def db(server_use_pickler: bool):
        # for the connection both work for GitHub Actions and concourse
>       db = gp.database(
            params={
                "host": environ.get("PGHOST", "localhost"),
                "dbname": environ.get("TESTDB", environ.get("USER")),
                "user": environ.get("PGUSER", environ.get("USER")),
                "password": environ.get("PGPASSWORD"),
            }
        )

tests/__init__.py:33:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
greenplumpython/db.py:281: in database
    return Database(uri=uri, params=params)
greenplumpython/db.py:41: in __init__
    self._conn = psycopg2.connect(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

dsn = 'host=localhost dbname=cc user=cc', connection_factory = None, cursor_factory = <class 'psycopg2.extras.RealDictCursor'>, kwargs = {}
kwasync = {}

    def connect(dsn=None, connection_factory=None, cursor_factory=None, **kwargs):
        """
        Create a new database connection.

        The connection parameters can be specified as a string:

            conn = psycopg2.connect("dbname=test user=postgres password=secret")

        or using a set of keyword arguments:

            conn = psycopg2.connect(database="test", user="postgres", password="secret")

        Or as a mix of both. The basic connection parameters are:

        - *dbname*: the database name
        - *database*: the database name (only as keyword argument)
        - *user*: user name used to authenticate
        - *password*: password used to authenticate
        - *host*: database host address (defaults to UNIX socket if not provided)
        - *port*: connection port number (defaults to 5432 if not provided)

        Using the *connection_factory* parameter a different class or connections
        factory can be specified. It should be a callable object taking a dsn
        argument.

        Using the *cursor_factory* parameter, a new default cursor factory will be
        used by cursor().

        Using *async*=True an asynchronous connection will be created. *async_* is
        a valid alias (for Python versions where ``async`` is a keyword).

        Any other keyword parameter will be passed to the underlying client
        library: the list of supported parameters depends on the library version.

        """
        kwasync = {}
        if 'async' in kwargs:
            kwasync['async'] = kwargs.pop('async')
        if 'async_' in kwargs:
            kwasync['async_'] = kwargs.pop('async_')

        dsn = _ext.make_dsn(dsn, **kwargs)
>       conn = _connect(dsn, connection_factory=connection_factory, **kwasync)
E       psycopg2.OperationalError: connection to server at "localhost" (127.0.0.1), port 65432 failed: Connection refused
E               Is the server running on that host and accepting TCP/IP connections?

.tox/test-container/lib/python3.11/site-packages/psycopg2/__init__.py:122: OperationalError
=========================================================== short test summary info ===========================================================
ERROR tests/test_binaryExpr.py::test_expr_bin_equal_int - psycopg2.OperationalError: connection to server at "localhost" (127.0.0.1), port 6...
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
============================================================== 1 error in 0.26s ===============================================================
test-container: exit 1 (0.43 seconds) /home/cc/repo/GreenplumPython> pytest --exitfirst pid=499107
.pkg: _exit> python /usr/lib/python3.11/site-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
  test-container: FAIL code 1 (2.90=setup[2.46]+cmd[0.43] seconds)
  evaluation failed :( (2.94 seconds)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Have you installed tox-docker? Could you please share the result of pip show, like

[gpadmin@localhost GreenplumPython]$ python3 -m pip show tox-docker
Name: tox-docker
Version: 4.1.0
Summary: Launch a docker instance around test runs
Home-page: https://github.com/tox-dev/tox-docker
Author: None
Author-email: None
License: UNKNOWN
Location: /home/gpadmin/.local/lib/python3.9/site-packages
Requires: tox, docker, packaging
Required-by:

Copy link
Collaborator

Choose a reason for hiding this comment

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

GreenplumPython on  ci_pgvector [$!?] via 🐍 v3.11.5 (venv311)
❯ tox -e test-container
.pkg: _optional_hooks> python /home/cc/gp/venv311/lib/python3.11/site-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
.pkg: get_requires_for_build_sdist> python /home/cc/gp/venv311/lib/python3.11/site-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
.pkg: get_requires_for_build_wheel> python /home/cc/gp/venv311/lib/python3.11/site-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
.pkg: prepare_metadata_for_build_wheel> python /home/cc/gp/venv311/lib/python3.11/site-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
.pkg: build_sdist> python /home/cc/gp/venv311/lib/python3.11/site-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
test-container: install_package> python -I -m pip install --force-reinstall --no-deps /home/cc/repo/GreenplumPython/.tox/.tmp/package/4/greenplum-python-1.0.1.tar.gz
test-container: docker> build server/postgres12-python39.Dockerfile
test-container: docker> built: sha256:dd26e95cbf86
test-container: docker> run 'sha256:dd26e95cbf86' (from 'server')
test-container: docker> health check None (from 'server')
test-container: commands[0]> pytest --exitfirst
============================================================= test session starts =============================================================
platform linux -- Python 3.11.5, pytest-7.0.1, pluggy-1.3.0
cachedir: .tox/test-container/.pytest_cache
rootdir: /home/cc/repo/GreenplumPython, configfile: tox.ini, testpaths: tests, greenplumpython
collected 241 items

tests/test_binaryExpr.py ........................                                                                                       [  9%]
tests/test_builtin.py ....                                                                                                              [ 11%]
tests/test_column.py ..                                                                                                                 [ 12%]
tests/test_dataframe.py ...................................                                                                             [ 26%]
tests/test_db.py ....                                                                                                                   [ 28%]
tests/test_file.py ...                                                                                                                  [ 29%]
tests/test_func.py .............................................                                                                        [ 48%]
tests/test_group.py ......                                                                                                              [ 51%]
tests/test_index.py ....                                                                                                                [ 52%]
tests/test_join.py ....................                                                                                                 [ 60%]
tests/test_order.py .......                                                                                                             [ 63%]
tests/test_pandas_api.py .......                                                                                                        [ 66%]
tests/test_schema.py .............                                                                                                      [ 72%]
tests/test_type.py ......                                                                                                               [ 74%]
tests/test_unaryExpr.py ....                                                                                                            [ 76%]
tests/test_use_pickler.py .F

================================================================== FAILURES ===================================================================
_________________________________________________________ test_pickler_outside_class __________________________________________________________

db = <greenplumpython.db.Database object at 0x7f525184c410>

    def test_pickler_outside_class(db: gp.Database):
        @dataclass
        class Int:
            val: int

        @gp.create_function
        def add_one(i: int) -> Int:
            return Int(i + 1)

>       for row in db.apply(lambda: add_one(1), expand=True):

tests/test_use_pickler.py:23:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
greenplumpython/dataframe.py:749: in __iter__
    self._contents = self._fetch()
greenplumpython/dataframe.py:873: in _fetch
    result = self._db._execute(to_json_dataframe._serialize())
greenplumpython/db.py:77: in _execute
    cursor.execute(query)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <cursor object at 0x7f5251e1f950; closed: -1>
query = 'WITH cte_b643155ad8c34892a3788ec616aa262b AS (SELECT "pg_temp"."func_2d2ef878474e415daa1e20efb0ea4ad4"( 1) AS func_3c...27aa50acd4c44ac696feaeb2c8611)::TEXT FROM cte_bb6e62df32f24138a1af74673fa70e31 AS cte_8cd27aa50acd4c44ac696feaeb2c8611'
vars = None

    def execute(self, query, vars=None):
        self.column_mapping = []
        self._query_executed = True
>       return super().execute(query, vars)
E       psycopg2.errors.ExternalRoutineException: NameError: name 'Int' is not defined
E       CONTEXT:  Traceback (most recent call last):
E         PL/Python function "func_2d2ef878474e415daa1e20efb0ea4ad4", line 16, in <module>
E           return GD['__func_2d2ef878474e415daa1e20efb0ea4ad4'](i=i)
E         PL/Python function "func_2d2ef878474e415daa1e20efb0ea4ad4", line 1, in __func_2d2ef878474e415daa1e20efb0ea4ad4
E       PL/Python function "func_2d2ef878474e415daa1e20efb0ea4ad4"

.tox/test-container/lib/python3.11/site-packages/psycopg2/extras.py:236: ExternalRoutineException
------------------------------------------------------------ Captured stdout setup ------------------------------------------------------------
SELECT version();

        CREATE EXTENSION IF NOT EXISTS plpython3u;
        CREATE EXTENSION IF NOT EXISTS vector;
        DROP SCHEMA IF EXISTS test CASCADE;
        CREATE SCHEMA test;

CREATE FUNCTION "pg_temp"."func_bc95268ebbaa4b439cc0bf8c8de20c46" ("requirements" "text") RETURNS "text" AS $$
try:
    return GD['__func_bc95268ebbaa4b439cc0bf8c8de20c46'](requirements=requirements)
except KeyError:
    try:
        import dill as __lib_442cad7c9adc439c8cd761aa13392dbd
        import sysconfig as __lib_faeda410372842de814cbafd0378a099
        import sys as __lib_b6a4cd7baa0246788b6f226c441ef0a0
        if __lib_faeda410372842de814cbafd0378a099.get_python_version() != '3.11':
            raise ModuleNotFoundError
        setattr(__lib_b6a4cd7baa0246788b6f226c441ef0a0.modules['plpy'], '_SD', SD)
        GD['__func_bc95268ebbaa4b439cc0bf8c8de20c46'] = __lib_442cad7c9adc439c8cd761aa13392dbd.loads(b'\x80\x04\x95\xe2\x03\x00\x00\x00\x00\x00\x00\x8c\ndill._dill\x94\x8c\x10_create_function\x94\x93\x94(h\x00\x8c\x0c_create_code\x94\x93\x94(C \x02\x02\x08\x01\x08\x02"\x02\x0c\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\xfa\x04\x08\x02\x01<\x01\x06\x01\x12\x010\xff\x94K\x01K\x00K\x00K\x06K\x06K\x03C\xdc\x97\x00d\x01d\x00l\x00}\x01d\x01d\x00l\x01}\x02|\x02j\x02\x00\x00\x00\x00\x00\x00\x00\x00s\nJ\x00d\x02\xa6\x00\x00\x00\xab\x00\x00\x00\x00\x00\x00\x00\x00\x00\x82\x01|\x02j\x02\x00\x00\x00\x00\x00\x00\x00\x00d\x03d\x04d\x05d\x06d\x07g\x06}\x03\t\x00|\x01\xa0\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00|\x03d\x08|\x01j\x04\x00\x00\x00\x00\x00\x00\x00\x00|\x00\xac\t\xa6\x04\x00\x00\xab\x04\x00\x00\x00\x00\x00\x00\x00\x00}\x04|\x04S\x00#\x00|\x01j\x05\x00\x00\x00\x00\x00\x00\x00\x00$\x00r\x19}\x05t\r\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00|\x05j\x07\x00\x00\x00\x00\x00\x00\x00\x00\xa6\x01\x00\x00\xab\x01\x00\x00\x00\x00\x00\x00\x00\x00\x82\x01d\x00}\x05~\x05w\x01w\x00x\x03Y\x00w\x01\x94(NK\x00\x8c2Python executable is required to install packages.\x94\x8c\x02-m\x94\x8c\x03pip\x94\x8c\x07install\x94\x8c\r--requirement\x94\x8c\n/dev/stdin\x94\x88\x8c\x04text\x94\x8c\x06stderr\x94\x8c\x05input\x94\x87\x94t\x94(\x8c\nsubprocess\x94\x8c\x03sys\x94\x8c\nexecutable\x94\x8c\x0ccheck_output\x94\x8c\x06STDOUT\x94\x8c\x12CalledProcessError\x94\x8c\tException\x94\x8c\x06stdout\x94t\x94(\x8c\x0crequirements\x94\x8c\x02sp\x94h\x13\x8c\x03cmd\x94\x8c\x06output\x94\x8c\x01e\x94t\x94\x8c//home/cc/repo/GreenplumPython/tests/__init__.py\x94\x8c\x0bpip_install\x94h"K\tC\xa0\x80\x00\xe0\x04\x1b\xd0\x04\x1b\xd0\x04\x1b\xd0\x04\x1b\xd8\x04\x0e\x80J\x80J\x80J\xe0\x0b\x0e\x8c>\xd0\x04O\xd0\x04O\xd0\x1bO\xd1\x04O\xd4\x04O\xd0\x04O\xe0\x08\x0b\x8c\x0e\xd8\x08\x0c\xd8\x08\r\xd8\x08\x11\xd8\x08\x17\xd8\x08\x14\xf0\r\x07\x0b\x06\x80C\xf0\x10\x04\x05"\xd8\x11\x13\x97\x1f\x92\x1f\xa0\x13\xa84\xb8\x02\xbc\t\xc8\x1c\x90\x1f\xd1\x11V\xd4\x11V\x88\x06\xd8\x0f\x15\x88\r\xf8\xd8\x0b\r\xd4\x0b \xf0\x00\x01\x05"\xf0\x00\x01\x05"\xf0\x00\x01\x05"\xdd\x0e\x17\x98\x01\x9c\x08\xd1\x0e!\xd4\x0e!\xd0\x08!\xf8\xf8\xf8\xf8\xf0\x03\x01\x05"\xf8\xf8\xf8\x94C\x17\xa8\x1fA\x08\x00\xc1\x08\nA+\x03\xc1\x12\x14A&\x03\xc1&\x05A+\x03\x94))t\x94R\x94}\x94\x8c\x08__name__\x94\x8c\x05tests\x94sh"NNt\x94R\x94}\x94}\x94\x8c\x0f__annotations__\x94}\x94(h\x1bh\x00\x8c\n_load_type\x94\x93\x94\x8c\x03str\x94\x85\x94R\x94\x8c\x06return\x94h4us\x86\x94bh\'\x8c\tException\x94h1h\x18\x85\x94R\x94s0.')
    except ModuleNotFoundError:
        exec("def __func_bc95268ebbaa4b439cc0bf8c8de20c46(requirements):\n    import subprocess as sp\n    import sys\n    assert sys.executable, 'Python executable is required to install packages.'\n    cmd = [sys.executable, '-m', 'pip', 'install', '--requirement', '/dev/stdin']\n    try:\n        output = sp.check_output(cmd, text=True, stderr=sp.STDOUT, input=requirements)\n        return output\n    except sp.CalledProcessError as e:\n        raise Exception(e.stdout)", globals())
        GD['__func_bc95268ebbaa4b439cc0bf8c8de20c46'] = globals()['__func_bc95268ebbaa4b439cc0bf8c8de20c46']
    return GD['__func_bc95268ebbaa4b439cc0bf8c8de20c46'](requirements=requirements)
$$ LANGUAGE plpython3u;
WITH cte_8f8392e1885645618b19385c16508cdb AS (SELECT "pg_temp"."func_bc95268ebbaa4b439cc0bf8c8de20c46"( 'dill==0.3.6')    ),cte_7ab3b55ec11d440492e9f9334eb25046 AS (SELECT cte_8f8392e1885645618b19385c16508cdb.* FROM cte_8f8392e1885645618b19385c16508cdb)SELECT to_json(cte_20d7fa2883664f4ca8aa9414de43cfa0)::TEXT FROM cte_7ab3b55ec11d440492e9f9334eb25046 AS cte_20d7fa2883664f4ca8aa9414de43cfa0
-------------------------------------------------------------------------------------------------------------------------------------------
 func_bc95268ebbaa4b439cc0bf8c8de20c46
-------------------------------------------------------------------------------------------------------------------------------------------
 Requirement already satisfied: dill==0.3.6 in /var/lib/postgresql/venv/lib/python3.9/site-packages (from -r /dev/stdin (line 1)) (0.3.6)

-------------------------------------------------------------------------------------------------------------------------------------------
(1 row)

------------------------------------------------------------ Captured stdout call -------------------------------------------------------------
CREATE TYPE "pg_temp"."type_c22184a1bbcb4b1ebc9127dfe09a96d0" AS (
val "int4"
);
CREATE FUNCTION "pg_temp"."func_2d2ef878474e415daa1e20efb0ea4ad4" ("i" "int4") RETURNS "type_c22184a1bbcb4b1ebc9127dfe09a96d0" AS $$
try:
    return GD['__func_2d2ef878474e415daa1e20efb0ea4ad4'](i=i)
except KeyError:
    try:
        import dill as __lib_b1f5ce1f8d534cad841a7e2116b4dd9a
        import sysconfig as __lib_9496a77386224c408e453c8a4acf966a
        import sys as __lib_7ada146a0dc04b058e9af4d9b611a895
        if __lib_9496a77386224c408e453c8a4acf966a.get_python_version() != '3.11':
            raise ModuleNotFoundError
        setattr(__lib_7ada146a0dc04b058e9af4d9b611a895.modules['plpy'], '_SD', SD)
        GD['__func_2d2ef878474e415daa1e20efb0ea4ad4'] = __lib_b1f5ce1f8d534cad841a7e2116b4dd9a.loads(b'\x80\x04\x95\xf9\n\x00\x00\x00\x00\x00\x00\x8c\ndill._dill\x94\x8c\x10_create_function\x94\x93\x94(h\x00\x8c\x0c_create_code\x94\x93\x94(C\x02\x04\x02\x94K\x01K\x00K\x00K\x01K\x04K\x13C \x95\x01\x97\x00\x02\x00\x89\x01|\x00d\x01z\x00\x00\x00\xa6\x01\x00\x00\xab\x01\x00\x00\x00\x00\x00\x00\x00\x00S\x00\x94NK\x01\x86\x94)\x8c\x01i\x94\x85\x94\x8c7/home/cc/repo/GreenplumPython/tests/test_use_pickler.py\x94\x8c\x07add_one\x94\x8c+test_pickler_outside_class.<locals>.add_one\x94K\x13C\x15\xf8\x80\x00\xe0\x0f\x12\x88s\x901\x90q\x915\x89z\x8cz\xd0\x08\x19\x94C\x00\x94\x8c\x03Int\x94\x85\x94)t\x94R\x94}\x94\x8c\x08__name__\x94\x8c\x16tests.test_use_pickler\x94sh\x0bNh\x00\x8c\x0c_create_cell\x94\x93\x94N\x85\x94R\x94\x85\x94t\x94R\x94}\x94}\x94(\x8c\x0f__annotations__\x94}\x94(h\x08h\x00\x8c\n_load_type\x94\x93\x94\x8c\x03int\x94\x85\x94R\x94\x8c\x06return\x94h\x00\x8c\x0c_create_type\x94\x93\x94(h"\x8c\x04type\x94\x85\x94R\x94h\x0fh"\x8c\x06object\x94\x85\x94R\x94\x85\x94}\x94(\x8c\n__module__\x94h\x15\x8c\x0f__annotations__\x94}\x94\x8c\x03val\x94h%s\x8c\x07__doc__\x94\x8c\rInt(val: int)\x94\x8c\x14__dataclass_params__\x94\x8c\x0bdataclasses\x94\x8c\x10_DataclassParams\x94\x93\x94)\x81\x94N}\x94(\x8c\x04init\x94\x88\x8c\x04repr\x94\x88\x8c\x02eq\x94\x88\x8c\x05order\x94\x89\x8c\x0bunsafe_hash\x94\x89\x8c\x06frozen\x94\x89u\x86\x94b\x8c\x14__dataclass_fields__\x94}\x94h4h8\x8c\x05Field\x94\x93\x94)\x81\x94N}\x94(\x8c\x04name\x94h4h)h%\x8c\x07default\x94cdataclasses\nMISSING\n\x8c\x0fdefault_factory\x94cdataclasses\nMISSING\nh>\x88\x8c\x04hash\x94Nh=\x88\x8c\x07compare\x94\x88\x8c\x08metadata\x94h"\x8c\x10MappingProxyType\x94\x85\x94R\x94}\x94\x85\x94R\x94\x8c\x07kw_only\x94\x89\x8c\x0b_field_type\x94cdataclasses\n_FIELD\nu\x86\x94bs\x8c\x08__init__\x94h\x02(h\x04(C\x02\x02\x01\x94K\x02K\x00K\x00K\x02K\x02K\x13C\x14\x97\x00|\x01|\x00_\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x00S\x00\x94N\x85\x94h4\x85\x94\x8c\x04self\x94h4\x86\x94\x8c\x08<string>\x94hY\x8c\x1f__create_fn__.<locals>.__init__\x94K\x02C\r\x80\x00\xd8\x0b\x0e\x80$\x84(\x80(\x80(\x94h\x0e))t\x94R\x94}\x94h\x14h\x15shYNNt\x94R\x94}\x94}\x94(h\x1f}\x94(h4h%h&Nu\x8c\x0c__qualname__\x94\x8c0test_pickler_outside_class.<locals>.Int.__init__\x94u\x86\x94b\x8c\x08__repr__\x94h\x02(h\x04(C\x10\x04\x02D\x01\x08\x01\x04\x01*\x01\x02\x01\x16\x02`\x01\x94K\x01K\x00K\x00K\x03K\x05K\x13C\xfa\x95\x02\x97\x00t\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00|\x00\xa6\x01\x00\x00\xab\x01\x00\x00\x00\x00\x00\x00\x00\x00t\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00j\x02\x00\x00\x00\x00\x00\x00\x00\x00\xa6\x00\x00\x00\xab\x00\x00\x00\x00\x00\x00\x00\x00\x00f\x02}\x01|\x01\x89\x03v\x00r\x02d\x01S\x00\x89\x03\xa0\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00|\x01\xa6\x01\x00\x00\xab\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\t\x00\x02\x00\x89\x04|\x00\xa6\x01\x00\x00\xab\x01\x00\x00\x00\x00\x00\x00\x00\x00}\x02\x89\x03\xa0\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00|\x01\xa6\x01\x00\x00\xab\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00n\x1a#\x00\x89\x03\xa0\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00|\x01\xa6\x01\x00\x00\xab\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00w\x00x\x03Y\x00w\x01|\x02S\x00\x94N\x8c\x03...\x94\x86\x94(\x8c\x02id\x94\x8c\x07_thread\x94\x8c\tget_ident\x94\x8c\x03add\x94\x8c\x07discard\x94t\x94h^\x8c\x03key\x94\x8c\x06result\x94\x87\x94\x8c"/usr/lib/python3.11/dataclasses.py\x94\x8c\x07wrapper\x94\x8c _recursive_repr.<locals>.wrapper\x94K\xe9C\x8a\xf8\x80\x00\xe5\x0e\x10\x90\x14\x89h\x8ch\x9d\x07\xd4\x18)\xd1\x18+\xd4\x18+\xd0\x0e+\x88\x03\xd8\x0b\x0e\x90,\xd0\x0b\x1e\xd0\x0b\x1e\xd8\x13\x18\x905\xd8\x08\x14\xd7\x08\x18\xd2\x08\x18\x98\x13\xd1\x08\x1d\xd4\x08\x1d\xd0\x08\x1d\xf0\x02\x03\t&\xd8\x15"\x90]\xa04\xd1\x15(\xd4\x15(\x88F\xe0\x0c\x18\xd7\x0c \xd2\x0c \xa0\x13\xd1\x0c%\xd4\x0c%\xd0\x0c%\xd0\x0c%\xf8\x88L\xd7\x0c \xd2\x0c \xa0\x13\xd1\x0c%\xd4\x0c%\xd0\x0c%\xd0\x0c%\xf8\xf8\xf8\xd8\x0f\x15\x88\r\x94C\x0c\xc1\x00\x0bA!\x00\xc1!\x17A8\x03\x94\x8c\x0crepr_running\x94\x8c\ruser_function\x94\x86\x94)t\x94R\x94}\x94h\x14h\x15shnNh\x17N\x85\x94R\x94h\x17N\x85\x94R\x94\x86\x94t\x94R\x94}\x94\x8c\x0b__wrapped__\x94h\x02(h\x04(C\x02\x02\x01\x94K\x01K\x00K\x00K\x01K\x04K\x13C2\x97\x00|\x00j\x00\x00\x00\x00\x00\x00\x00\x00\x00j\x01\x00\x00\x00\x00\x00\x00\x00\x00d\x01|\x00j\x02\x00\x00\x00\x00\x00\x00\x00\x00\x9b\x02d\x02\x9d\x03z\x00\x00\x00S\x00\x94N\x8c\x05(val=\x94\x8c\x01)\x94\x87\x94\x8c\t__class__\x94hkh4\x87\x94h^\x85\x94h`hn\x8c\x1f__create_fn__.<locals>.__repr__\x94K\x02C \x80\x00\xd8\t\r\x8c\x1e\xd4\t$\xd0\'<\xa8t\xacx\xd0\'<\xd0\'<\xd0\'<\xd1\t<\xd0\x02<\x94h\x0e))t\x94R\x94}\x94h\x14h\x15shnNNt\x94R\x94}\x94}\x94(h\x1f}\x94hkh\x98u\x86\x94bs}\x94(h\x1fh\xa1hk\x8c0test_pickler_outside_class.<locals>.Int.__repr__\x94u\x86\x94bh\x86(\x8c\x02id\x94\x8c\x08builtins\x94\x8c\x02id\x94\x93\x94\x8c\x07_thread\x94h\x00\x8c\x0e_import_module\x94\x93\x94ht\x85\x94R\x94u0\x8c\x06__eq__\x94h\x02(h\x04(C\x06\x02\x01\x1c\x01$\x01\x94K\x02K\x00K\x00K\x02K\x02K\x13CP\x97\x00|\x01j\x00\x00\x00\x00\x00\x00\x00\x00\x00|\x00j\x00\x00\x00\x00\x00\x00\x00\x00\x00u\x00r\x12|\x00j\x01\x00\x00\x00\x00\x00\x00\x00\x00f\x01|\x01j\x01\x00\x00\x00\x00\x00\x00\x00\x00f\x01k\x02\x00\x00\x00\x00S\x00t\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00S\x00\x94N\x85\x94h\x95h4\x8c\x0eNotImplemented\x94\x87\x94h^\x8c\x05other\x94\x86\x94h`h\xaf\x8c\x1d__create_fn__.<locals>.__eq__\x94K\x02C*\x80\x00\xd8\x05\n\x84_\x98\x04\x9c\x0e\xd0\x05&\xd0\x05&\xd8\x0b\x0f\x8c8\x88+\x98\x05\x9c\t\x90|\xd2\n#\xd0\x03#\xdd\t\x17\xd0\x02\x17\x94h\x0e))t\x94R\x94}\x94h\x14h\x15sh\xafNNt\x94R\x94}\x94}\x94(h\x1f}\x94hk\x8c.test_pickler_outside_class.<locals>.Int.__eq__\x94u\x86\x94bh\xbb\x8c\x0eNotImplemented\x94h\x00\x8c\n_eval_repr\x94\x93\x94\x8c\x0eNotImplemented\x94\x85\x94R\x94s0\x8c\x08__hash__\x94N\x8c\x0e__match_args__\x94h4\x85\x94ut\x94R\x94h\xa7\x8c\x07setattr\x94\x93\x94h\xcdhk\x8c\'test_pickler_outside_class.<locals>.Int\x94\x87\x94R0uhkh\x0cu\x86\x94bh\xa7\x8c\x07getattr\x94\x93\x94\x8c\x04dill\x94\x8c\x05_dill\x94\x93\x94\x8c\x08_setattr\x94h\xcf\x87\x94R\x94h\x8a\x8c\rcell_contents\x94h\x9e\x87\x94R0h\xdah\x88h\xdb\x8f\x94\x87\x94R0h\xdah\x19h\xdbh\xcd\x87\x94R0.')
    except ModuleNotFoundError:
        exec("def __func_2d2ef878474e415daa1e20efb0ea4ad4(i):\n    return Int(i + 1)", globals())
        GD['__func_2d2ef878474e415daa1e20efb0ea4ad4'] = globals()['__func_2d2ef878474e415daa1e20efb0ea4ad4']
    return GD['__func_2d2ef878474e415daa1e20efb0ea4ad4'](i=i)
$$ LANGUAGE plpython3u;
WITH cte_b643155ad8c34892a3788ec616aa262b AS (SELECT "pg_temp"."func_2d2ef878474e415daa1e20efb0ea4ad4"( 1) AS func_3c00a917e5f74b59bb48887ca4279826   ),cte_bb6e62df32f24138a1af74673fa70e31 AS (SELECT (cte_b643155ad8c34892a3788ec616aa262b."func_3c00a917e5f74b59bb48887ca4279826").* FROM cte_b643155ad8c34892a3788ec616aa262b)SELECT to_json(cte_8cd27aa50acd4c44ac696feaeb2c8611)::TEXT FROM cte_bb6e62df32f24138a1af74673fa70e31 AS cte_8cd27aa50acd4c44ac696feaeb2c8611
=========================================================== short test summary info ===========================================================
FAILED tests/test_use_pickler.py::test_pickler_outside_class - psycopg2.errors.ExternalRoutineException: NameError: name 'Int' is not defined
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
======================================================= 1 failed, 185 passed in 20.15s ========================================================
test-container: exit 1 (20.33 seconds) /home/cc/repo/GreenplumPython> pytest --exitfirst pid=593035
test-container: docker> remove '7e4af2ef8be8' (from 'server')
.pkg: _exit> python /home/cc/gp/venv311/lib/python3.11/site-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
  test-container: FAIL code 1 (126.25=setup[105.92]+cmd[20.33] seconds)
  evaluation failed :( (126.34 seconds)

Then I met antoher error. Python3.11.

Copy link
Collaborator

@beeender beeender Nov 13, 2023

Choose a reason for hiding this comment

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

test-container: docker> build server/postgres12-python39.Dockerfile
test-container: docker> built: sha256:dd26e95cbf86
test-container: docker> run 'sha256:dd26e95cbf86' (from 'server')
test-container: docker> health check None (from 'server')
test-container: commands[0]> pytest --exitfirst
============================================================= test session starts =============================================================
platform linux -- Python 3.11.5, pytest-7.0.1, pluggy-1.3.0
cachedir: .tox/test-container/.pytest_cache
rootdir: /home/cc/repo/GreenplumPython, configfile: tox.ini, testpaths: tests, greenplumpython
collected 241 items

Why it builds a python39 image, but runs a python311 tests?

Copy link
Contributor Author

@xuebinsu xuebinsu Nov 13, 2023

Choose a reason for hiding this comment

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

This means the server uses python3.9, while the client uses python3.11.

In this case the python versions don't match so dill will not work. In that case, the CI skips test_use_pickler.py since the file contains test cases that can only be passed when dill works.

Copy link
Collaborator

Choose a reason for hiding this comment

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

OK, so when running docker test, the client is not running in the container, only the pg server, correct?

Copy link
Contributor Author

@xuebinsu xuebinsu Nov 14, 2023

Choose a reason for hiding this comment

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

Yes, that's correct.

```

Run a specified test case(s):
Expand Down
3 changes: 1 addition & 2 deletions concourse/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@ _main() {
unset PYTHONPATH
unset PYTHONHOME
python3.9 -m pip install tox
python3.9 -m pip install .
tox -e test_py39
tox -e test
popd
}

Expand Down
14 changes: 7 additions & 7 deletions greenplumpython/dataframe.py
Original file line number Diff line number Diff line change
Expand Up @@ -798,9 +798,9 @@ def refresh(self) -> "DataFrame":
.. highlight:: python
.. code-block:: Python

>>> cursor.execute("DROP TABLE IF EXISTS const_table;")
>>> cursor.execute("DROP TABLE IF EXISTS t_refresh;")
>>> nums = db.create_dataframe(rows=[(i,) for i in range(5)], column_names=["num"])
>>> df = nums.save_as("const_table", column_names=["num"], temp=False).order_by("num")[:]
>>> df = nums.save_as("t_refresh", column_names=["num"], temp=False).order_by("num")[:]
>>> df
-----
num
Expand All @@ -812,7 +812,7 @@ def refresh(self) -> "DataFrame":
4
-----
(5 rows)
>>> cursor.execute("INSERT INTO const_table(num) VALUES (5);")
>>> cursor.execute("INSERT INTO t_refresh(num) VALUES (5);")
>>> df
-----
num
Expand All @@ -836,7 +836,7 @@ def refresh(self) -> "DataFrame":
5
-----
(6 rows)
>>> cursor.execute("DROP TABLE const_table;")
>>> cursor.execute("DROP TABLE t_refresh;")

Note:
`cursor` is a predefined `Psycopg Cursor <https://www.psycopg.org/docs/cursor.html>`_
Expand Down Expand Up @@ -913,7 +913,7 @@ def save_as(
.. code-block:: Python

>>> nums = db.create_dataframe(rows=[(i,) for i in range(5)], column_names=["num"])
>>> df = nums.save_as("const_table", column_names=["num"], temp=True)
>>> df = nums.save_as("t_saved", column_names=["num"], temp=True)
>>> df.order_by("num")[:]
-----
num
Expand All @@ -925,8 +925,8 @@ def save_as(
4
-----
(5 rows)
>>> const_table = db.create_dataframe(table_name="const_table")
>>> const_table.order_by("num")[:]
>>> t_saved = db.create_dataframe(table_name="t_saved")
>>> t_saved.order_by("num")[:]
-----
num
-----
Expand Down
4 changes: 1 addition & 3 deletions greenplumpython/experimental/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,9 @@ def _archive_and_upload(tmp_archive_name: str, files: list[str], db: gp.Database
def _from_files(_, files: list[str], parser: NormalFunction, db: gp.Database) -> gp.DataFrame:
tmp_archive_name = f"tar_{uuid.uuid4().hex}"
_archive_and_upload(tmp_archive_name, files, db)
func_sig = inspect.signature(parser.unwrap())
result_members = get_type_hints(func_sig.return_annotation)
return db.apply(
lambda: parser(_extract_files(tmp_archive_name, "files")),
expand=len(result_members) == 0,
expand=True,
)


Expand Down
1 change: 0 additions & 1 deletion requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
black==22.3.0
isort==5.10.1
pytest==7.0.1
pglast==3.10
pyright==1.1.250
pandas
1 change: 0 additions & 1 deletion requirements-doc.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
-r requirements.txt
sphinx==5.1.1
sphinx_rtd_theme==1.0.0
nbsphinx
Expand Down
21 changes: 21 additions & 0 deletions server/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/bin/bash

set -o nounset -o xtrace -o errexit -o pipefail

PG_MAJOR_VERSION=$(pg_config --version | grep --only-matching --extended-regexp '[0-9]+' | head -n 1)
export DEBIAN_FRONTEND=nointeractive
apt-get update
apt-get install --no-install-recommends -y \
postgresql-plpython3-"$PG_MAJOR_VERSION" \
postgresql-"$PG_MAJOR_VERSION"-pgvector \
python3-pip \
python3-venv
apt-get autoclean

POSTGRES_USER_SITE=$(su --login postgres --session-command "python3 -m site --user-site")
POSTGRES_USER_BASE=$(su --login postgres --session-command "python3 -m site --user-base")
mkdir --parents "$POSTGRES_USER_SITE"
chown --recursive postgres "$POSTGRES_USER_BASE"

cp /tmp/initdb.sh /docker-entrypoint-initdb.d
chown postgres /docker-entrypoint-initdb.d/*
12 changes: 12 additions & 0 deletions server/initdb.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/bash

set -o nounset -o xtrace -o errexit -o pipefail

{
echo "logging_collector = ON"
echo "log_statement = 'all'"
echo "log_destination = 'csvlog'"
} >>"$PGDATA"/postgresql.conf

python3 -m venv "$HOME"/venv
source "$HOME"/venv/bin/activate
11 changes: 11 additions & 0 deletions server/postgres12-python311.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FROM postgres:12-bookworm

COPY build.sh initdb.sh /tmp/
RUN bash /tmp/build.sh

HEALTHCHECK --interval=1s --timeout=1s --start-period=1s --retries=30 CMD psql \
--single-transaction \
--user=$POSTGRES_USER \
--dbname=$POSTGRES_DB \
--host=localhost \
--command="SELECT version();"
11 changes: 11 additions & 0 deletions server/postgres12-python39.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FROM postgres:12-bullseye

COPY build.sh initdb.sh /tmp/
RUN bash /tmp/build.sh

HEALTHCHECK --interval=1s --timeout=1s --start-period=1s --retries=30 CMD psql \
--single-transaction \
--user=$POSTGRES_USER \
--dbname=$POSTGRES_DB \
--host=localhost \
--command="SELECT version();"
46 changes: 39 additions & 7 deletions tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,58 @@
import greenplumpython as gp


@pytest.fixture()
def db():
# NOTE: This UDF must **not** depend on picklers, such as dill.
@gp.create_function
def pip_install(requirements: str) -> str:
import subprocess as sp
import sys

assert sys.executable, "Python executable is required to install packages."
cmd = [
sys.executable,
"-m",
"pip",
"install",
"--requirement",
"/dev/stdin",
]
try:
output = sp.check_output(cmd, text=True, stderr=sp.STDOUT, input=requirements)
return output
except sp.CalledProcessError as e:
raise Exception(e.stdout)


@pytest.fixture(scope="session")
def db(server_use_pickler: bool):
# for the connection both work for GitHub Actions and concourse
db = gp.database(
params={
"host": environ.get("POSTGRES_HOST", "localhost"),
"dbname": environ.get("TESTDB", "gpadmin"),
"user": environ.get("PGUSER"),
"host": environ.get("PGHOST", "localhost"),
"dbname": environ.get("TESTDB", environ.get("USER")),
"user": environ.get("PGUSER", environ.get("USER")),
"password": environ.get("PGPASSWORD"),
}
)
db._execute("DROP SCHEMA IF EXISTS test CASCADE; CREATE SCHEMA test;", has_results=False)
db._execute(
"""
CREATE EXTENSION IF NOT EXISTS plpython3u;
CREATE EXTENSION IF NOT EXISTS vector;
DROP SCHEMA IF EXISTS test CASCADE;
CREATE SCHEMA test;
""",
has_results=False,
)
if server_use_pickler:
print(db.apply(lambda: pip_install("dill==0.3.6")))
yield db
db.close()


@pytest.fixture()
def con():
host = "localhost"
dbname = environ.get("TESTDB", "gpadmin")
dbname = environ.get("TESTDB", environ.get("USER"))
con = f"postgresql://{host}/{dbname}"
yield con

Expand Down
16 changes: 16 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import pytest


def pytest_addoption(parser: pytest.Parser):
parser.addini(
"server_use_pickler",
type="bool",
default=True,
help="Use pickler to deserialize UDFs on server.",
)


@pytest.fixture(scope="session")
def server_use_pickler(pytestconfig: pytest.Config) -> bool:
val: bool = pytestconfig.getini("server_use_pickler")
return val
Loading
Loading