diff --git a/python/Ice/customError/.gitignore b/python/Ice/customError/.gitignore new file mode 100644 index 000000000..0533745ca --- /dev/null +++ b/python/Ice/customError/.gitignore @@ -0,0 +1,2 @@ +*_ice.py +VisitorCenter diff --git a/python/Ice/customError/README.md b/python/Ice/customError/README.md new file mode 100644 index 000000000..4233b0600 --- /dev/null +++ b/python/Ice/customError/README.md @@ -0,0 +1,91 @@ +# Ice Custom Error + +The Custom error demo shows how to define an exception in Slice, and how to throw and catch this exception. + +A Slice-defined exception should be seen as a custom error carried by the response instead of the expected return +value--there is naturally no throwing across the network. + +We recommend running each program in a separate Python virtual environment. If you are new to Python virtual environments, +see [Python Virtual Environments]. + +## Running the server + +Navigate to the `server` directory. + +### 1. Create and activate a Python virtual environment + +#### macOS and Linux + +```bash +python3 -m venv venv +source venv/bin/activate +``` + +#### Windows (PowerShell) + +```powershell +python -m venv venv +venv\Scripts\activate +``` + +### 2. Install program dependencies + +```bash +pip install -r requirements.txt +``` + +### 3. Compile the Slice definitions + +Use the Slice-to-Python compiler to generate Python code from the `Greeter.ice` file: + +```bash +slice2py ../slice/Greeter.ice +``` + +### 4. Run the server + +```bash +python main.py +``` + +## Running the client + +In a separate terminal, navigate to the `client` directory. + +### 1. Create and activate a Python virtual environment + +#### macOS and Linux + +```bash +python3 -m venv venv +source venv/bin/activate +``` + +#### Windows (PowerShell) + +```powershell +python -m venv venv +venv\Scripts\activate +``` + +### 2. Install program dependencies + +```bash +pip install -r requirements.txt +``` + +### 3. Compile the Slice definitions + +Use the Slice-to-Python compiler to generate Python code from the `Greeter.ice` file: + +```bash +slice2py ../slice/Greeter.ice +``` + +### 4. Run the client + +```bash +python main.py +``` + +[Python Virtual Environments]: https://docs.python.org/3/tutorial/venv.html diff --git a/python/Ice/customError/client/main.py b/python/Ice/customError/client/main.py new file mode 100755 index 000000000..bf31688ba --- /dev/null +++ b/python/Ice/customError/client/main.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python +# Copyright (c) ZeroC, Inc. + +import asyncio +import getpass +import sys + +import Ice + +# Slice module VisitorCenter in Greeter.ice maps to Python module VisitorCenter. +import VisitorCenter + + +async def main(): + # Create an Ice communicator. We'll use this communicator to create proxies, and manage outgoing connections. We + # enable asyncio support by passing the current event loop to initialize. + async with Ice.initialize(sys.argv, eventLoop=asyncio.get_running_loop()) as communicator: + # GreeterPrx is a class generated by the Slice compiler. We create a proxy from a communicator and a "stringified + # proxy" with the address of the target object. + # If you run the server on a different computer, replace localhost in the string below with the server's hostname + # or IP address. + greeter = VisitorCenter.GreeterPrx(communicator, "greeter:tcp -h localhost -p 4061") + + names = [getpass.getuser(), "", "alice", "bob", "carol", "dave", "billy bob"] + + for name in names: + # Send a request to the remote object and get the response. The response from the server can carry: + # - a greeting (success) + # - a dispatch exception (the base class for marshallable system exceptions), or + # - a GreeterException (the custom exception we've defined in the Slice definitions) + try: + greeting = await greeter.greetAsync(name) + print(greeting) + except Ice.DispatchException as exception: + print( + f"Failed to create a greeting for '{name}': DispatchException {{ message = '{exception.args}', replyStatus = {exception.replyStatus} }}" + ) + except VisitorCenter.GreeterException as exception: + print( + f"Failed to create a greeting for '{name}': GreeterException {{ message = '{exception.message}', error = {exception.error} }}" + ) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/python/Ice/customError/client/requirements.txt b/python/Ice/customError/client/requirements.txt new file mode 100644 index 000000000..5fdf3aa17 --- /dev/null +++ b/python/Ice/customError/client/requirements.txt @@ -0,0 +1,11 @@ +# Use ZeroC Nightly repository as the main index for pip. +--index-url https://download.zeroc.com/nexus/repository/pypi-nightly/simple/ + +# Allow installing additional packages from PyPI main index if not found in the ZeroC repository. +--extra-index-url https://pypi.org/simple/ + +# Allow installing pre-release versions (required for zeroc-ice nightly builds). +--pre + +# Add the zeroc-ice package (latest nightly version). This package includes the Slice to Python compiler (slice2py). +zeroc-ice diff --git a/python/Ice/customError/server/chatbot.py b/python/Ice/customError/server/chatbot.py new file mode 100644 index 000000000..366923e6c --- /dev/null +++ b/python/Ice/customError/server/chatbot.py @@ -0,0 +1,47 @@ +# Copyright (c) ZeroC, Inc. + +from datetime import datetime, timedelta + +import Ice + +# Slice module VisitorCenter in Greeter.ice maps to Python module VisitorCenter. +import VisitorCenter + +MAX_LENGTH = 7 + + +class Chatbot(VisitorCenter.Greeter): + """ + Chatbot is an Ice servant that implements Slice interface Greeter. + """ + + # Implements the method greet from the Greeter class generated by the Slice compiler. + def greet(self, name: str, current: Ice.Current) -> str: + print(f"Dispatching greet request {{ name = '{name}' }}") + + if len(name) > MAX_LENGTH: + raise VisitorCenter.GreeterException("Name is longer than maximum!", VisitorCenter.GreeterError.NameTooLong) + + match name: + case "": + # ObjectNotExistException is a dispatch exception with a reply status of 'ObjectNotExist'. + raise Ice.ObjectNotExistException() + + case "alice": + # Simulate an authentication error by throwing a dispatch exception with the Unauthorized error code. + # Note: This is a demo; no real authentication logic is implemented. + raise Ice.DispatchException( + Ice.ReplyStatus.Unauthorized, "Invalid credentials. The administrator has been notified." + ) + case "bob": + raise VisitorCenter.GreeterException( + f"Away until {(datetime.now() + timedelta(minutes=5)).strftime('%Y-%m-%d %H:%M:%S')}.", + VisitorCenter.GreeterError.Away, + ) + case "carol": + raise VisitorCenter.GreeterException( + "I am already greeting someone else.", VisitorCenter.GreeterError.GreetingOtherVisitor + ) + + case _: + return f"Hello, {name}!" diff --git a/python/Ice/customError/server/main.py b/python/Ice/customError/server/main.py new file mode 100755 index 000000000..0ffdbd628 --- /dev/null +++ b/python/Ice/customError/server/main.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python +# Copyright (c) ZeroC, Inc. + +import sys + +import chatbot +import Ice + + +def main(): + # Create an Ice communicator. We'll use this communicator to create an object adapter. + with Ice.initialize(sys.argv) as communicator: + # Create an object adapter that listens for incoming requests and dispatches them to servants. + adapter = communicator.createObjectAdapterWithEndpoints("GreeterAdapter", "tcp -p 4061") + + # Register the Chatbot servant with the adapter. + adapter.add(chatbot.Chatbot(), Ice.Identity(name="greeter")) + + # Start dispatching requests. + adapter.activate() + print("Listening on port 4061...") + + try: + # Wait until communicator.shutdown() is called, which never occurs in this demo. + communicator.waitForShutdown() + except KeyboardInterrupt: + print("Caught Ctrl+C, exiting...") + + +if __name__ == "__main__": + main() diff --git a/python/Ice/customError/server/requirements.txt b/python/Ice/customError/server/requirements.txt new file mode 100644 index 000000000..5fdf3aa17 --- /dev/null +++ b/python/Ice/customError/server/requirements.txt @@ -0,0 +1,11 @@ +# Use ZeroC Nightly repository as the main index for pip. +--index-url https://download.zeroc.com/nexus/repository/pypi-nightly/simple/ + +# Allow installing additional packages from PyPI main index if not found in the ZeroC repository. +--extra-index-url https://pypi.org/simple/ + +# Allow installing pre-release versions (required for zeroc-ice nightly builds). +--pre + +# Add the zeroc-ice package (latest nightly version). This package includes the Slice to Python compiler (slice2py). +zeroc-ice diff --git a/python/Ice/customError/slice/Greeter.ice b/python/Ice/customError/slice/Greeter.ice new file mode 100644 index 000000000..94e30e1a6 --- /dev/null +++ b/python/Ice/customError/slice/Greeter.ice @@ -0,0 +1,28 @@ +// Copyright (c) ZeroC, Inc. + +module VisitorCenter +{ + /// Represents the error code carried by GreeterException. + enum GreeterError { NameTooLong, Away, GreetingOtherVisitor } + + /// Represents the custom error returned by the Greeter when it cannot create a greeting. This exception is thrown + /// by the implementation of the greet operation and caught by the caller. + exception GreeterException + { + /// Describes the exception in a human-readable way. + string message; + + /// Provides an error code that can be used to handle this exception programmatically. + GreeterError error; + } + + /// Represents a simple greeter. + interface Greeter + { + /// Creates a personalized greeting. + /// @param name The name of the person to greet. + /// @return The greeting. + /// @throws GreeterException Thrown when the greeter cannot create a greeting. + string greet(string name) throws GreeterException; + } +} diff --git a/python/README.md b/python/README.md index dfbbfb3ea..69d72c84b 100644 --- a/python/README.md +++ b/python/README.md @@ -11,6 +11,7 @@ demonstrates a specific feature or programming technique. | [Ice Callback](./Ice/callback/) | Shows how to implement callbacks in a client application. | | [Ice Config](./Ice/config/) | Shows how to configure client and server applications using Ice configuration files. | | [Ice Context](./Ice/context/) | Shows how to set and retrieve request contexts. | +| [Ice Custom Error](./Ice/customError/) | Shows how to define a new exception in Slice and return this exception from a Slice operation. | | [Ice Greeter](./Ice/greeter/) | Shows how to call and implement a canonical Greeter application with Ice. **Start with this demo!** | | [Ice Inheritance](./Ice/inheritance/) | Shows the power of interface inheritance in Slice. | | [Ice Invocation Timeout](./Ice/invocation_timeout/) | Shows how to use invocation timeouts |