Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,9 @@ io_file
# IPython notebook checkpoints
.ipynb_checkpoints

# Python virtual env
venv/

# Editor temporaries
*.swp
*~
Expand Down
1 change: 1 addition & 0 deletions tools/pythonpkg/duckdb-stubs/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ __version__: str

__interactive__: bool
__jupyter__: bool
__formatted_python_version__: str

class BinderException(ProgrammingError): ...

Expand Down
2 changes: 2 additions & 0 deletions tools/pythonpkg/duckdb/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@
__standard_vector_size__,
__interactive__,
__jupyter__,
__formatted_python_version__,
__version__,
apilevel,
comment,
Expand All @@ -269,6 +270,7 @@
"__standard_vector_size__",
"__interactive__",
"__jupyter__",
"__formatted_python_version__",
"__version__",
"apilevel",
"comment",
Expand Down
1 change: 1 addition & 0 deletions tools/pythonpkg/duckdb_python.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1079,6 +1079,7 @@ PYBIND11_MODULE(DUCKDB_PYTHON_LIB_NAME, m) { // NOLINT
m.attr("__git_revision__") = DuckDB::SourceID();
m.attr("__interactive__") = DuckDBPyConnection::DetectAndGetEnvironment();
m.attr("__jupyter__") = DuckDBPyConnection::IsJupyter();
m.attr("__formatted_python_version__") = DuckDBPyConnection::FormattedPythonVersion();
m.def("default_connection", &DuckDBPyConnection::DefaultConnection,
"Retrieve the connection currently registered as the default to be used by the module");
m.def("set_default_connection", &DuckDBPyConnection::SetDefaultConnection,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ struct DuckDBPyConnection : public enable_shared_from_this<DuckDBPyConnection> {

static bool DetectAndGetEnvironment();
static bool IsJupyter();
static std::string FormattedPythonVersion();
static shared_ptr<DuckDBPyConnection> DefaultConnection();
static void SetDefaultConnection(shared_ptr<DuckDBPyConnection> conn);
static PythonImportCache *ImportCache();
Expand Down Expand Up @@ -357,6 +358,7 @@ struct DuckDBPyConnection : public enable_shared_from_this<DuckDBPyConnection> {
vector<unique_ptr<SQLStatement>> GetStatements(const py::object &query);

static PythonEnvironmentType environment;
static std::string formatted_python_version;
static void DetectEnvironment();
};

Expand Down
16 changes: 14 additions & 2 deletions tools/pythonpkg/src/pyconnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ DefaultConnectionHolder DuckDBPyConnection::default_connection;
DBInstanceCache instance_cache; // NOLINT: allow global
shared_ptr<PythonImportCache> DuckDBPyConnection::import_cache = nullptr; // NOLINT: allow global
PythonEnvironmentType DuckDBPyConnection::environment = PythonEnvironmentType::NORMAL; // NOLINT: allow global
std::string DuckDBPyConnection::formatted_python_version = "";

DuckDBPyConnection::~DuckDBPyConnection() {
try {
Expand All @@ -83,6 +84,13 @@ DuckDBPyConnection::~DuckDBPyConnection() {
}

void DuckDBPyConnection::DetectEnvironment() {
// Get the formatted Python version
py::module_ sys = py::module_::import("sys");
py::object version_info = sys.attr("version_info");
int major = py::cast<int>(version_info.attr("major"));
int minor = py::cast<int>(version_info.attr("minor"));
DuckDBPyConnection::formatted_python_version = std::to_string(major) + "." + std::to_string(minor);

// If __main__ does not have a __file__ attribute, we are in interactive mode
auto main_module = py::module_::import("__main__");
if (py::hasattr(main_module, "__file__")) {
Expand Down Expand Up @@ -120,6 +128,10 @@ bool DuckDBPyConnection::IsJupyter() {
return DuckDBPyConnection::environment == PythonEnvironmentType::JUPYTER;
}

std::string DuckDBPyConnection::FormattedPythonVersion() {
return DuckDBPyConnection::formatted_python_version;
}

// NOTE: this function is generated by tools/pythonpkg/scripts/generate_connection_methods.py.
// Do not edit this function manually, your changes will be overwritten!

Expand Down Expand Up @@ -2146,9 +2158,9 @@ shared_ptr<DuckDBPyConnection> DuckDBPyConnection::Connect(const py::object &dat
"If set, restores the old behavior of scanning all preceding frames to locate the referenced variable.",
LogicalType::BOOLEAN, Value::BOOLEAN(false));
if (!DuckDBPyConnection::IsJupyter()) {
config_dict["duckdb_api"] = Value("python");
config_dict["duckdb_api"] = Value("python/" + DuckDBPyConnection::FormattedPythonVersion());
} else {
config_dict["duckdb_api"] = Value("python jupyter");
config_dict["duckdb_api"] = Value("python/" + DuckDBPyConnection::FormattedPythonVersion() + " jupyter");
}
config.SetOptionsByName(config_dict);

Expand Down
4 changes: 2 additions & 2 deletions tools/pythonpkg/tests/fast/api/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,15 @@ def test_incorrect_parameter(self, duckdb_cursor):

def test_user_agent_default(self, duckdb_cursor):
con_regular = duckdb.connect(':memory:')
regex = re.compile("duckdb/.* python")
regex = re.compile("duckdb/.* python/.*")
# Expands to: SELECT * FROM pragma_user_agent()
assert regex.match(con_regular.sql("PRAGMA user_agent").fetchone()[0]) is not None
custom_user_agent = con_regular.sql("SELECT current_setting('custom_user_agent')").fetchone()
assert custom_user_agent[0] == ''

def test_user_agent_custom(self, duckdb_cursor):
con_regular = duckdb.connect(':memory:', config={'custom_user_agent': 'CUSTOM_STRING'})
regex = re.compile("duckdb/.* python CUSTOM_STRING")
regex = re.compile("duckdb/.* python/.* CUSTOM_STRING")
assert regex.match(con_regular.sql("PRAGMA user_agent").fetchone()[0]) is not None
custom_user_agent = con_regular.sql("SELECT current_setting('custom_user_agent')").fetchone()
assert custom_user_agent[0] == 'CUSTOM_STRING'
Expand Down
8 changes: 8 additions & 0 deletions tools/pythonpkg/tests/fast/test_duckdb_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import duckdb
import sys


def test_duckdb_api():
res = duckdb.execute("SELECT name, value FROM duckdb_settings() WHERE name == 'duckdb_api'")
formatted_python_version = f"{sys.version_info.major}.{sys.version_info.minor}"
assert res.fetchall() == [('duckdb_api', f'python/{formatted_python_version}')]
6 changes: 6 additions & 0 deletions tools/pythonpkg/tests/fast/test_version.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import duckdb
import sys


def test_version():
assert duckdb.__version__ != "0.0.0"


def test_formatted_python_version():
formatted_python_version = f"{sys.version_info.major}.{sys.version_info.minor}"
assert duckdb.__formatted_python_version__ == formatted_python_version
Loading